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

commit 02e8a2c14c389f302ec811c84756af33315c639b
Author: Andi Huber <[email protected]>
AuthorDate: Sat Apr 25 21:25:19 2020 +0200

    ISIS-2340: share logic of ObjectAdapterAccessHelper/UpdateHelper (8)
---
 .../metamodel/spec/interaction/ActionHandle.java   |  44 +++
 .../spec/interaction/CollectionHandle.java         |  45 +++
 .../spec/interaction/InteractionVeto.java          |  50 +--
 .../metamodel/spec/interaction/ManagedAction.java  |  83 +++++
 .../spec/interaction/ManagedCollection.java        |  90 +++++
 .../metamodel/spec/interaction/ManagedMember.java  | 130 +++++++
 .../spec/interaction/ManagedProperty.java          | 109 ++++++
 .../metamodel/spec/interaction/MemberHandle.java   |  81 ++++
 .../metamodel/spec/interaction/PropertyHandle.java |  48 +++
 .../viewer/vaadin/ui/binding/BinderUtil.java       |   6 +-
 .../vaadin/ui/components/collection/TableView.java |  14 +-
 .../ui/components/object/ObjectFormView.java       |  59 +--
 .../common/model/binding/UiComponentFactory.java   |  17 +-
 .../binding/interaction/ActionAccessChain.java     |  53 ---
 .../model/binding/interaction/ActionBinding.java   |  25 --
 .../binding/interaction/ActionBindingReadonly.java |  19 -
 .../binding/interaction/ActionBindingUsable.java   |  19 -
 .../binding/interaction/CollectionAccessChain.java |  54 ---
 .../binding/interaction/CollectionBinding.java     |  25 --
 .../interaction/CollectionBindingEditable.java     |  19 -
 .../interaction/CollectionBindingReadonly.java     |  19 -
 .../model/binding/interaction/ObjectBinding.java   | 406 +++------------------
 .../binding/interaction/PropertyAccessChain.java   |  56 ---
 .../model/binding/interaction/PropertyBinding.java |  31 --
 .../interaction/PropertyBindingEditable.java       |  37 --
 .../interaction/PropertyBindingReadonly.java       |  27 --
 .../resources/DomainObjectResourceServerside.java  |  30 +-
 .../viewer/resources/DomainResourceHelper.java     |  14 +-
 .../resources/InteractionFailureHandler.java       |  56 ++-
 .../resources/ObjectAdapterAccessHelper.java       |  71 +++-
 30 files changed, 875 insertions(+), 862 deletions(-)

diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionHandle.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionHandle.java
new file mode 100644
index 0000000..7f71884
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ActionHandle.java
@@ -0,0 +1,44 @@
+/*
+ *  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.core.metamodel.spec.interaction;
+
+import org.apache.isis.core.commons.internal.base._Either;
+
+import lombok.NonNull;
+
+public final class ActionHandle extends MemberHandle<ManagedAction, 
ActionHandle> {
+
+    ActionHandle(@NonNull _Either<ManagedAction, InteractionVeto> chain) {
+        super(chain);
+    }
+
+//    public PropertyHandle modifyProperty(
+//            @NonNull final Function<ManagedProperty, ManagedObject> 
newProperyValueProvider) {
+//
+//        chain = chain.leftRemap(property->{
+//            val validityVeto = 
property.modifyProperty(newProperyValueProvider.apply(property));
+//            return validityVeto.isPresent()
+//                ? _Either.right(validityVeto.get()) 
+//                : _Either.left(property); 
+//        });
+//        return this;
+//    }
+
+    
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/CollectionHandle.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/CollectionHandle.java
new file mode 100644
index 0000000..c08f041
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/CollectionHandle.java
@@ -0,0 +1,45 @@
+/*
+ *  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.core.metamodel.spec.interaction;
+
+import org.apache.isis.core.commons.internal.base._Either;
+
+import lombok.NonNull;
+
+public final class CollectionHandle extends MemberHandle<ManagedCollection, 
CollectionHandle> {
+
+    CollectionHandle(@NonNull _Either<ManagedCollection, InteractionVeto> 
chain) {
+        super(chain);
+    }
+
+//    public PropertyHandle modifyProperty(
+//            @NonNull final Function<ManagedProperty, ManagedObject> 
newProperyValueProvider) {
+//
+//        chain = chain.leftRemap(property->{
+//            val validityVeto = 
property.modifyProperty(newProperyValueProvider.apply(property));
+//            return validityVeto.isPresent()
+//                ? _Either.right(validityVeto.get()) 
+//                : _Either.left(property); 
+//        });
+//        return this;
+//    }
+
+    
+}
+
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/InteractionResponse.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java
similarity index 50%
rename from 
viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/InteractionResponse.java
rename to 
core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java
index feee940..61e4e88 100644
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/InteractionResponse.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/InteractionVeto.java
@@ -16,49 +16,55 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.viewer.common.model.binding.interaction;
+package org.apache.isis.core.metamodel.spec.interaction;
+
+import java.io.Serializable;
+
+import org.apache.isis.core.metamodel.consent.Consent;
 
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 
+@Getter
 @RequiredArgsConstructor(staticName = "of", access = AccessLevel.PRIVATE)
-public class InteractionResponse {
-    
-    public static enum Veto {
+public class InteractionVeto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static enum VetoType {
         NOT_FOUND,
         HIDDEN,
-        FORBIDDEN,
-        UNAUTHORIZED, 
+        READONLY,
+        INVALID, 
     }
     
-    private final static InteractionResponse SUCCESS = of(null, null);
-    @Getter private final Veto veto;
-    @Getter private final String failureMessage;
+    @NonNull private final VetoType vetoType;
+    @NonNull private final Consent vetoConsent;
     
-    public boolean isSuccess() {
-        return veto==null;
+    public static InteractionVeto notFound(@NonNull Consent vetoConsent) {
+        return of(VetoType.NOT_FOUND, vetoConsent);
     }
     
-    public boolean isFailure() {
-        return !isSuccess();
+    public static InteractionVeto hidden(@NonNull Consent vetoConsent) {
+        return of(VetoType.HIDDEN, vetoConsent);
     }
     
-    // -- FACTORIES
-    
-    public static InteractionResponse failed(@NonNull Veto veto) {
-        return of(veto, "unspecified");
+    public static InteractionVeto readonly(@NonNull Consent vetoConsent) {
+        return of(VetoType.READONLY, vetoConsent);
     }
     
-    public static InteractionResponse failed(@NonNull Veto veto, String 
reason) {
-        return of(veto, reason);
+    public static InteractionVeto invalid(@NonNull Consent vetoConsent) {
+        return of(VetoType.INVALID, vetoConsent);
     }
 
-    public static InteractionResponse success() {
-        return SUCCESS;
+    public String getReason() {
+        return getVetoConsent().getReason();
     }
-
     
+    public String getDescription() {
+        return getVetoConsent().getDescription();
+    }
     
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java
new file mode 100644
index 0000000..2e88681
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedAction.java
@@ -0,0 +1,83 @@
+/*
+ *  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.core.metamodel.spec.interaction;
+
+import java.util.Optional;
+
+import org.apache.isis.core.commons.internal.base._Either;
+import org.apache.isis.core.metamodel.consent.Veto;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.val;
+
+public final class ManagedAction extends ManagedMember {
+
+    // -- FACTORIES
+    
+    public static final Optional<ManagedAction> lookupAction(
+            @NonNull final ManagedObject owner,
+            @NonNull final String memberId) {
+        
+        return ManagedMember.<ObjectAction>lookup(owner, MemberType.ACTION, 
memberId)
+        .map(objectAction -> new ManagedAction(owner, objectAction));
+    }
+    
+    public static final ActionHandle getActionHandle(
+            @NonNull final ManagedObject owner,
+            @NonNull final String memberId) {
+    
+        val managedAction = ManagedAction.lookupAction(owner, memberId);
+        
+        final _Either<ManagedAction, InteractionVeto> chain = 
managedAction.isPresent()
+                ? _Either.left(managedAction.get())
+                : _Either.right(InteractionVeto.notFound(new 
Veto(notFound(MemberType.ACTION, memberId))));
+                
+        return new ActionHandle(chain);
+    }
+
+    // -- IMPLEMENTATION
+    
+    @Getter private final ObjectAction action;
+    
+    private ManagedAction(
+            final @NonNull ManagedObject owner, 
+            final @NonNull ObjectAction action) {
+        
+        super(owner);
+        this.action = action;
+    }
+
+    @Override
+    public ObjectAction getMember() {
+        return getAction();
+    }
+
+    @Override
+    public MemberType getMemberType() {
+        return MemberType.ACTION;
+    }
+
+    public String getName() {
+        return getAction().getName();
+    }
+
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedCollection.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedCollection.java
new file mode 100644
index 0000000..e51b322
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedCollection.java
@@ -0,0 +1,90 @@
+/*
+ *  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.core.metamodel.spec.interaction;
+
+import java.util.Optional;
+
+import org.apache.isis.core.commons.internal.base._Either;
+import org.apache.isis.core.metamodel.consent.Veto;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.val;
+
+public final class ManagedCollection extends ManagedMember {
+
+    // -- FACTORIES
+    
+    public static final Optional<ManagedCollection> lookupCollection(
+            @NonNull final ManagedObject owner,
+            @NonNull final String memberId) {
+        
+        return ManagedMember.<OneToManyAssociation>lookup(owner, 
MemberType.COLLECTION, memberId)
+        .map(objectAction -> new ManagedCollection(owner, objectAction));
+    }
+    
+    public static final CollectionHandle getCollectionHandle(
+            @NonNull final ManagedObject owner,
+            @NonNull final String memberId) {
+    
+        val managedCollection = ManagedCollection.lookupCollection(owner, 
memberId);
+        
+        final _Either<ManagedCollection, InteractionVeto> chain = 
managedCollection.isPresent()
+                ? _Either.left(managedCollection.get())
+                : _Either.right(InteractionVeto.notFound(new 
Veto(notFound(MemberType.COLLECTION, memberId))));
+                
+        return new CollectionHandle(chain);
+    }
+    
+    // -- IMPLEMENTATION
+    
+    @Getter private final OneToManyAssociation collection;
+    
+    private ManagedCollection(
+            final @NonNull ManagedObject owner, 
+            final @NonNull OneToManyAssociation collection) {
+        
+        super(owner);
+        this.collection = collection;
+    }
+
+    @Override
+    public OneToManyAssociation getMember() {
+        return getCollection();
+    }
+
+    @Override
+    public MemberType getMemberType() {
+        return MemberType.COLLECTION;
+    }
+
+    public String getName() {
+        return getCollection().getName();
+    }
+
+    public ManagedObject getCollectionValue() {
+        val collection = getCollection();
+        
+        return Optional.ofNullable(collection.get(getOwner()))
+        .orElse(ManagedObject.of(collection.getSpecification(), null));
+    }
+
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedMember.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedMember.java
new file mode 100644
index 0000000..f9dc991
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedMember.java
@@ -0,0 +1,130 @@
+/*
+ *  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.core.metamodel.spec.interaction;
+
+import java.util.Optional;
+import java.util.function.BiFunction;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.commons.internal.base._Casts;
+import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+
+@RequiredArgsConstructor
+public abstract class ManagedMember {
+
+    // only used to create failure messages
+    @RequiredArgsConstructor
+    public static enum MemberType {
+        PROPERTY(OneToOneAssociation.class, (spec, propertyId)->
+        spec.getAssociation(propertyId)
+        .map(property->property.isOneToOneAssociation()?property:null)),
+        
+        COLLECTION(OneToManyAssociation.class, (spec, collectionId)->
+        spec.getAssociation(collectionId)
+        .map(collection->collection.isOneToManyAssociation()?collection:null)),
+        
+        ACTION(ObjectAction.class, (spec, actionId)->
+        spec.getObjectAction(actionId));
+        
+        @Getter private final Class<? extends ObjectMember> memberType;
+        private final BiFunction<
+                ObjectSpecification, String, 
+                Optional<? extends ObjectMember>
+            > memberProvider;
+    
+        public <T extends ObjectMember> Optional<T> lookup(
+                @NonNull final ManagedObject owner,
+                @NonNull final String memberId) {
+            val onwerSpec = owner.getSpecification();
+            val member = memberProvider.apply(onwerSpec, memberId);
+            return _Casts.uncheckedCast(member);
+        }
+        
+    }
+    
+    @Getter
+    private final ManagedObject owner;
+    
+    public abstract ObjectMember getMember();
+    
+    public abstract MemberType getMemberType();
+    
+    public ObjectSpecification getSpecification() {
+        return getMember().getSpecification();
+    }
+    
+    /**
+     * @param where
+     * @return non-empty if hidden
+     */
+    public Optional<InteractionVeto> checkVisibility(
+            @NonNull final Where where) {
+
+        val visibilityConsent = 
+                getMember()
+                .isVisible(getOwner(), InteractionInitiatedBy.USER, where);
+        
+        return visibilityConsent.isVetoed()
+                ? Optional.of(InteractionVeto.hidden(visibilityConsent)) 
+                : Optional.empty();
+    }
+
+    /**
+     * @param where
+     * @return non-empty if not usable/editable (meaning if read-only)
+     */
+    public Optional<InteractionVeto> checkUsability(
+            @NonNull final Where where) {
+        
+        val usabilityConsent = 
+                getMember()
+                .isUsable(getOwner(), InteractionInitiatedBy.USER, where);
+        
+        return usabilityConsent.isVetoed()
+                ? Optional.of(InteractionVeto.readonly(usabilityConsent))
+                : Optional.empty();
+    }
+    
+    protected static <T extends ObjectMember> Optional<T> lookup(
+            @NonNull final ManagedObject owner,
+            @NonNull final MemberType memberType,
+            @NonNull final String memberId) {
+        return memberType.lookup(owner, memberId);
+    }
+    
+    protected static <T extends ObjectMember> String notFound(
+            MemberType memberType, 
+            String memberId) {
+        return String.format("%s '%s' either does not exist, is disabled or is 
not visible", 
+                        memberId, 
+                        memberType.name().toLowerCase());
+    }
+    
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedProperty.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedProperty.java
new file mode 100644
index 0000000..6471f4b
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/ManagedProperty.java
@@ -0,0 +1,109 @@
+/*
+ *  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.core.metamodel.spec.interaction;
+
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import org.apache.isis.core.commons.internal.base._Either;
+import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.isis.core.metamodel.consent.Veto;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.val;
+
+public final class ManagedProperty extends ManagedMember {
+
+    // -- FACTORIES
+    
+    public static final Optional<ManagedProperty> lookupProperty(
+            @NonNull final ManagedObject owner,
+            @NonNull final String memberId) {
+        
+        return ManagedMember.<OneToOneAssociation>lookup(owner, 
MemberType.PROPERTY, memberId)
+        .map(objectAction -> new ManagedProperty(owner, objectAction));
+    }
+    
+    public static final PropertyHandle getPropertyHandle(
+            @NonNull final ManagedObject owner,
+            @NonNull final String memberId) {
+    
+        val managedProperty = ManagedProperty.lookupProperty(owner, memberId);
+        
+        final _Either<ManagedProperty, InteractionVeto> chain = 
managedProperty.isPresent()
+                ? _Either.left(managedProperty.get())
+                : _Either.right(InteractionVeto.notFound(new 
Veto(notFound(MemberType.PROPERTY, memberId))));
+                
+        return new PropertyHandle(chain);
+    }
+    
+    // -- IMPLEMENTATION
+    
+    @Getter private final OneToOneAssociation property;
+    
+    private ManagedProperty(
+            final @NonNull ManagedObject owner, 
+            final @NonNull OneToOneAssociation property) {
+        
+        super(owner);
+        this.property = property;
+    }
+
+    @Override
+    public OneToOneAssociation getMember() {
+        return getProperty();
+    }
+
+    @Override
+    public MemberType getMemberType() {
+        return MemberType.PROPERTY;
+    }
+
+    // -- INTERACTION
+    
+    /**
+     * @param proposedNewValue
+     * @return non-empty if the interaction is not valid for given {@code 
proposedNewValue}
+     */
+    public Optional<InteractionVeto> modifyProperty(@Nullable ManagedObject 
proposedNewValue) {
+            
+        val validityVeto = property.isAssociationValid(getOwner(), 
proposedNewValue, InteractionInitiatedBy.USER);
+        if (validityVeto.isVetoed()) {
+            return Optional.of(InteractionVeto.invalid(validityVeto));
+        }
+        
+        property.set(getOwner(), proposedNewValue, 
InteractionInitiatedBy.USER);
+        return Optional.empty();
+    }
+
+    public ManagedObject getPropertyValue() {
+        val property = getProperty();
+        
+        return Optional.ofNullable(property.get(getOwner()))
+        .orElse(ManagedObject.of(property.getSpecification(), null));
+    }
+
+
+    
+    
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberHandle.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberHandle.java
new file mode 100644
index 0000000..c3d6594
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/MemberHandle.java
@@ -0,0 +1,81 @@
+/*
+ *  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.core.metamodel.spec.interaction;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.commons.internal.base._Casts;
+import org.apache.isis.core.commons.internal.base._Either;
+
+import lombok.NonNull;
+import lombok.val;
+
+public abstract class MemberHandle<T extends ManagedMember, H extends 
MemberHandle<T, ?>> {
+
+    @NonNull protected _Either<T, InteractionVeto> chain;
+
+    protected MemberHandle(@NonNull _Either<T, InteractionVeto> chain) {
+        this.chain = chain;
+    }
+    
+    public H checkVisibility(@NonNull final Where where) {
+        chain = chain.leftRemap(property->{
+            val visibilityVeto = property.checkVisibility(where);
+            return visibilityVeto.isPresent()
+                ? _Either.right(visibilityVeto.get()) 
+                : _Either.left(property); 
+        });
+        return _Casts.uncheckedCast(this);
+    }
+    
+    public H checkUsability(@NonNull final Where where) {
+        chain = chain.leftRemap(property->{
+            val usablitiyVeto = property.checkUsability(where);
+            return usablitiyVeto.isPresent()
+                ? _Either.right(usablitiyVeto.get()) 
+                : _Either.left(property); 
+        });
+        return _Casts.uncheckedCast(this);
+    }
+    
+    @Deprecated
+    public H peekOnFailure(Consumer<InteractionVeto> onFailure) {
+        chain.right().ifPresent(onFailure);
+        return _Casts.uncheckedCast(this);
+    }
+
+    public <X extends Throwable> 
+    T getOrElseThrow(Function<InteractionVeto, ? extends X> onFailure) throws 
X {
+        val value = chain.leftIfAny();
+        if (value != null) {
+            return value;
+        } else {
+            throw onFailure.apply(chain.rightIfAny());
+        }
+    }
+    
+    public Optional<T> get() {
+        return chain.left();
+    }
+    
+    
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/PropertyHandle.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/PropertyHandle.java
new file mode 100644
index 0000000..c77e1e1
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/interaction/PropertyHandle.java
@@ -0,0 +1,48 @@
+/*
+ *  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.core.metamodel.spec.interaction;
+
+import java.util.function.Function;
+
+import org.apache.isis.core.commons.internal.base._Either;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+
+import lombok.NonNull;
+import lombok.val;
+
+public final class PropertyHandle extends MemberHandle<ManagedProperty, 
PropertyHandle> {
+
+    PropertyHandle(@NonNull _Either<ManagedProperty, InteractionVeto> chain) {
+        super(chain);
+    }
+
+    public PropertyHandle modifyProperty(
+            @NonNull final Function<ManagedProperty, ManagedObject> 
newProperyValueProvider) {
+
+        chain = chain.leftRemap(property->{
+            val validityVeto = 
property.modifyProperty(newProperyValueProvider.apply(property));
+            return validityVeto.isPresent()
+                ? _Either.right(validityVeto.get()) 
+                : _Either.left(property); 
+        });
+        return this;
+    }
+
+    
+}
diff --git 
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java
 
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java
index 6b458c5..b90b1f6 100644
--- 
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java
+++ 
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java
@@ -30,6 +30,7 @@ import com.vaadin.flow.data.converter.Converter;
 import com.vaadin.flow.data.converter.DateToSqlDateConverter;
 import com.vaadin.flow.data.converter.LocalDateToDateConverter;
 
+import org.apache.isis.core.metamodel.spec.interaction.InteractionVeto;
 import org.apache.isis.viewer.common.model.binding.UiComponentFactory.Request;
 
 import lombok.RequiredArgsConstructor;
@@ -109,7 +110,10 @@ public final class BinderUtil {
         @Override
         public Result<P> convertToModel(P newValue, ValueContext context) {
             // propagate new value down the domain model, and handle 
validation feedback
-            val validationMessage = 
request.getPropagator(pojoType).apply(newValue);
+            val validationMessage = 
request.getPropagator(pojoType).apply(newValue)
+                    .map(InteractionVeto::getReason)
+                    .orElse(null);
+                                        
             return validationMessage==null
                     ? Result.ok(newValue)
                     : Result.error(validationMessage);
diff --git 
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/collection/TableView.java
 
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/collection/TableView.java
index 99135b0..be05900 100644
--- 
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/collection/TableView.java
+++ 
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/collection/TableView.java
@@ -25,6 +25,7 @@ import java.util.stream.Collectors;
 
 import javax.annotation.Nullable;
 
+import com.vaadin.flow.component.Component;
 import com.vaadin.flow.component.grid.Grid;
 import com.vaadin.flow.component.orderedlayout.VerticalLayout;
 
@@ -34,6 +35,7 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.interaction.ManagedCollection;
 
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
@@ -70,14 +72,12 @@ public class TableView extends VerticalLayout {
     }
     
     /**
-     * Constructs a (page-able) {@link Grid} from given {@code 
objectAssociation} and {@code assocObject}   
-     * @param objectAssociation
-     * @param assocObject
+     * Constructs a (page-able) {@link Grid} from given {@code 
managedCollection}   
+     * @param managedCollection
      */
-    public static TableView fromObjectAssociation(
-            @NonNull final ObjectAssociation objectAssociation,
-            @Nullable final ManagedObject assocObject) {
+    public static Component fromManagedCollection(ManagedCollection 
managedCollection) {
         
+        val assocObject = managedCollection.getOwner();
         val assocObjectSpecification = assocObject.getSpecification();
         val collectionFacet = 
assocObjectSpecification.getFacet(CollectionFacet.class);
 
@@ -158,4 +158,6 @@ public class TableView extends VerticalLayout {
             return 
Optional.ofNullable(e.getMessage()).orElse(e.getClass().getName());
         }
     }
+
+
 }
diff --git 
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
 
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
index d531fbb..3e6b8d0 100644
--- 
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
+++ 
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
@@ -19,7 +19,6 @@
 package org.apache.isis.incubator.viewer.vaadin.ui.components.object;
 
 import java.util.Collection;
-import java.util.Optional;
 
 import com.vaadin.flow.component.Component;
 import com.vaadin.flow.component.HasComponents;
@@ -48,7 +47,9 @@ import org.apache.isis.applib.layout.grid.bootstrap3.BS3Row;
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab;
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.interaction.ManagedAction;
+import org.apache.isis.core.metamodel.spec.interaction.ManagedCollection;
+import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty;
 import 
org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentFactoryVaa;
 import 
org.apache.isis.incubator.viewer.vaadin.ui.components.collection.TableView;
 import org.apache.isis.viewer.common.model.binding.UiComponentFactory;
@@ -170,38 +171,51 @@ public class ObjectFormView extends VerticalLayout {
 
             @Override
             protected void onAction(HasComponents container, ActionLayoutData 
actionData) {
-                objectInteractor.lookupAction(actionData.getId())
-                .ifPresent(action->{
-                    val uiAction = new Button(action.getName());
+                
+                val owner = objectInteractor.getManagedObject();
+                ManagedAction.getActionHandle(owner, actionData.getId())
+                .checkVisibility(Where.OBJECT_FORMS)
+                .get()
+                .ifPresent(managedAction -> {
+                    // TODO yet not doing anything
+                    val uiAction = new Button(managedAction.getName());
                     container.add(uiAction);
                     uiAction.getStyle().set("margin-left", "0.5em");
                     uiAction.addThemeVariants(
                             ButtonVariant.LUMO_SMALL);
-
                 });
             }
 
             @Override
             protected void onProperty(HasComponents container, 
PropertyLayoutData propertyData) {
-                objectInteractor
-                .getPropertyBinding(propertyData.getId(), Where.OBJECT_FORMS)
-                .ifPresent(propertyBinding->{
+                
+                val owner = objectInteractor.getManagedObject();
+                
+                ManagedProperty.getPropertyHandle(owner, propertyData.getId())
+                .checkVisibility(Where.OBJECT_FORMS)
+                .get()
+                .ifPresent(managedProperty -> {
                     val uiProperty = uiComponentFactory
-                            
.componentFor(UiComponentFactory.Request.of(propertyBinding));
-                    container.add(uiProperty);                    
+                            
.componentFor(UiComponentFactory.Request.of(managedProperty));
+                    container.add(uiProperty);
                 });
             }
 
             @Override
             protected void onCollection(HasComponents container, 
CollectionLayoutData collectionData) {
-                objectInteractor.lookupCollection(collectionData.getId())
-                .ifPresent(collection->{
-                    container.add(new H3(collection.getName()));
-
-                    val collectionValue = 
Optional.ofNullable(collection.get(managedObject))
-                            
.orElse(ManagedObject.of(collection.getSpecification(), null));
-                    container.add(createCollectionComponent(collection, 
collectionValue));
+                
+                val owner = objectInteractor.getManagedObject();
+                
+                ManagedCollection.getCollectionHandle(owner, 
collectionData.getId())
+                .checkVisibility(Where.OBJECT_FORMS)
+                .get()
+                .ifPresent(managedCollection -> {
+                    container.add(new H3(managedCollection.getName()));
+                    
+                    val uiCollection = 
createCollectionComponent(managedCollection);
+                    container.add(uiCollection);
                 });
+                
             }
 
         };
@@ -243,13 +257,12 @@ public class ObjectFormView extends VerticalLayout {
     // -- HELPER
 
     private Component createCollectionComponent(
-            final ObjectAssociation objectAssociation,
-            final ManagedObject assocObject) {
+            final ManagedCollection managedCollection) {
 
-        val labelLiteral = "Collection: " + objectAssociation.getName();
-        val pojo = assocObject.getPojo();
+        val labelLiteral = "Collection: " + managedCollection.getName();
+        val pojo = managedCollection.getCollectionValue().getPojo();
         if (pojo instanceof Collection) {
-            return TableView.fromObjectAssociation(objectAssociation, 
assocObject);
+            return TableView.fromManagedCollection(managedCollection);
         }
 
         if (pojo == null) {
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java
index fbff986..ef6892e 100644
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java
+++ 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java
@@ -30,7 +30,8 @@ import 
org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectFeature;
-import org.apache.isis.viewer.common.model.binding.interaction.PropertyBinding;
+import org.apache.isis.core.metamodel.spec.interaction.InteractionVeto;
+import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty;
 
 import lombok.NonNull;
 import lombok.Value;
@@ -47,21 +48,15 @@ public interface UiComponentFactory<T> {
         /** not null but the wrapped pojo is allowed to be null*/
         @NonNull private final ManagedObject managedObject; 
         @NonNull private final ObjectFeature objectFeature;
-        @NonNull private final Function<ManagedObject, String> 
toDomainPropagator;
+        @NonNull private final Function<ManagedObject, 
Optional<InteractionVeto> > toDomainPropagator;
         
-        public static Request of(PropertyBinding propertyBinding) {
+        public static Request of(ManagedProperty propertyBinding) {
             val property = propertyBinding.getProperty();
             //TODO there is also the aspect of editable or not
             val propertyValue = propertyBinding.getPropertyValue();
             
             return of(propertyValue, property, proposedNewValue -> {
-
-                val iResponse = 
propertyBinding.modifyProperty(proposedNewValue);
-                if (iResponse.isFailure()) {
-                    return iResponse.getFailureMessage(); // validation result 
if any
-                }
-                return null;
-
+                return propertyBinding.modifyProperty(proposedNewValue);
             });
         }
         
@@ -135,7 +130,7 @@ public interface UiComponentFactory<T> {
                     .map(type::cast);
         }
 
-        public <T> Function<T, String> getPropagator(Class<T> pojoType) {
+        public <T> Function<T, Optional<InteractionVeto>> 
getPropagator(Class<T> pojoType) {
             return (T newValueProposal)->{
                 //TODO we are loosing any fields that are cached within 
ManagedObject
                 val newValue = ManagedObject.of(getFeatureSpec(), 
newValueProposal);
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionAccessChain.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionAccessChain.java
deleted file mode 100644
index a082ead..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionAccessChain.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import java.util.function.Consumer;
-
-import org.apache.isis.core.commons.internal.base._Either;
-import org.apache.isis.core.commons.internal.exceptions._Exceptions;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(staticName = "of")
-public class ActionAccessChain {
-    
-    @NonNull private _Either<ActionBinding, InteractionResponse> chain;
-
-//    public ActionAccessChain modifyProperty(
-//            @NonNull final Function<OneToOneAssociation, ManagedObject> 
newProperyValueProvider) {
-//
-//        chain = chain.leftRemap(propertyBinding->{
-//            final InteractionResponse iResponse = 
propertyBinding.modifyProperty(newProperyValueProvider);
-//            if(iResponse.isFailure()) {
-//                _Either.right(iResponse);
-//            }
-//            return _Either.left(propertyBinding);
-//        });
-//        
-//        return this;
-//    }
-
-    public ActionAccessChain onFailure(Consumer<InteractionResponse> 
onFailure) {
-        chain.right().ifPresent(onFailure);
-        return this;
-    }
-
-    public ActionBinding getBinding() {
-        return chain.left()
-                .orElseThrow(_Exceptions::noSuchElement);
-    }
-    
-    public ObjectAction getAction() {
-        return chain.left()
-                .map(ActionBinding::getAction)
-                .orElseThrow(_Exceptions::noSuchElement);
-    }
-
-    public ActionAccessChain ifPresent(Consumer<ActionBinding> 
actionBindingConsumer) {
-        chain.left().ifPresent(actionBindingConsumer::accept);
-        return this;
-    }
-    
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBinding.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBinding.java
deleted file mode 100644
index 45aee47..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBinding.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-
-public interface ActionBinding {
-
-    ManagedObject getManagedObject();
-    ObjectAction getAction();
-
-//    InteractionResponse modifyProperty(ManagedObject newProperyValue);
-//    
-//    default InteractionResponse modifyProperty(
-//            @NonNull final Function<OneToOneAssociation, ManagedObject> 
newProperyValueProvider) {
-//        return modifyProperty(newProperyValueProvider.apply(getProperty()));
-//    }
-//    
-//    default ManagedObject getPropertyValue() {
-//        val property = getProperty();
-//        
-//        return Optional.ofNullable(property.get(getManagedObject()))
-//        .orElse(ManagedObject.of(property.getSpecification(), null));
-//    }
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingReadonly.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingReadonly.java
deleted file mode 100644
index 09f5b55..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingReadonly.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(staticName = "of")
-public class ActionBindingReadonly implements ActionBinding {
-
-    @Getter(onMethod = @__(@Override))
-    private final ManagedObject managedObject;
-    
-    @Getter(onMethod = @__(@Override))
-    private final ObjectAction action;
-    
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingUsable.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingUsable.java
deleted file mode 100644
index 2bdc815..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ActionBindingUsable.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(staticName = "of")
-public class ActionBindingUsable implements ActionBinding {
-
-    @Getter(onMethod = @__(@Override))
-    private final ManagedObject managedObject;
-    
-    @Getter(onMethod = @__(@Override))
-    private final ObjectAction action;
-    
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionAccessChain.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionAccessChain.java
deleted file mode 100644
index e00fff7..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionAccessChain.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import java.util.function.Consumer;
-
-import org.apache.isis.core.commons.internal.base._Either;
-import org.apache.isis.core.commons.internal.exceptions._Exceptions;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(staticName = "of")
-public class CollectionAccessChain {
-    
-    @NonNull private _Either<CollectionBinding, InteractionResponse> chain;
-
-//    public CollectionAccessChain modifyProperty(
-//            @NonNull final Function<OneToManyAssociation, ManagedObject> 
newProperyValueProvider) {
-//
-//        chain = chain.leftRemap(propertyBinding->{
-//            final InteractionResponse iResponse = 
propertyBinding.modifyProperty(newProperyValueProvider);
-//            if(iResponse.isFailure()) {
-//                _Either.right(iResponse);
-//            }
-//            return _Either.left(propertyBinding);
-//        });
-//        
-//        return this;
-//    }
-
-    public CollectionAccessChain onFailure(Consumer<InteractionResponse> 
onFailure) {
-        chain.right().ifPresent(onFailure);
-        return this;
-    }
-
-    public CollectionBinding getBinding() {
-        return chain.left()
-                .orElseThrow(_Exceptions::noSuchElement);
-    }
-    
-    public OneToManyAssociation getCollection() {
-        return chain.left()
-                .map(CollectionBinding::getCollection)
-                .orElseThrow(_Exceptions::noSuchElement);
-    }
-
-    public CollectionAccessChain ifPresent(Consumer<CollectionBinding> 
collectionBindingConsumer) {
-        chain.left().ifPresent(collectionBindingConsumer::accept);
-        return this;
-    }
-
-    
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBinding.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBinding.java
deleted file mode 100644
index 92de7cd..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBinding.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-
-public interface CollectionBinding {
-
-    ManagedObject getManagedObject();
-    OneToManyAssociation getCollection();
-
-//    InteractionResponse modifyProperty(ManagedObject newProperyValue);
-//    
-//    default InteractionResponse modifyProperty(
-//            @NonNull final Function<OneToOneAssociation, ManagedObject> 
newProperyValueProvider) {
-//        return modifyProperty(newProperyValueProvider.apply(getProperty()));
-//    }
-//    
-//    default ManagedObject getPropertyValue() {
-//        val property = getProperty();
-//        
-//        return Optional.ofNullable(property.get(getManagedObject()))
-//        .orElse(ManagedObject.of(property.getSpecification(), null));
-//    }
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingEditable.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingEditable.java
deleted file mode 100644
index 877e654..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingEditable.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(staticName = "of")
-public class CollectionBindingEditable implements CollectionBinding {
-
-    @Getter(onMethod = @__(@Override))
-    private final ManagedObject managedObject;
-    
-    @Getter(onMethod = @__(@Override))
-    private final OneToManyAssociation collection;
-    
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingReadonly.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingReadonly.java
deleted file mode 100644
index d2a4b03..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/CollectionBindingReadonly.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(staticName = "of")
-public class CollectionBindingReadonly implements CollectionBinding {
-
-    @Getter(onMethod = @__(@Override))
-    private final ManagedObject managedObject;
-    
-    @Getter(onMethod = @__(@Override))
-    private final OneToManyAssociation collection;
-    
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectBinding.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectBinding.java
index 64f2a81..20931d2 100644
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectBinding.java
+++ 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectBinding.java
@@ -18,62 +18,14 @@
  */
 package org.apache.isis.viewer.common.model.binding.interaction;
 
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.BiFunction;
-import java.util.function.UnaryOperator;
-import java.util.stream.Stream;
-
-import org.apache.isis.applib.annotation.Where;
-import org.apache.isis.core.commons.internal.base._Either;
-import org.apache.isis.core.commons.internal.exceptions._Exceptions;
-import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.spec.feature.Contributed;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import 
org.apache.isis.viewer.common.model.binding.interaction.InteractionResponse.Veto;
 
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
-import lombok.val;
 
 @RequiredArgsConstructor(staticName = "bind")
 public class ObjectBinding {
     
-    // only used to create failure messages
-    @RequiredArgsConstructor
-    private static enum MemberType {
-        PROPERTY(OneToOneAssociation.class, (spec, propertyId)->
-        spec.getAssociation(propertyId)
-        .map(property->property.isOneToOneAssociation()?property:null)),
-        
-        COLLECTION(OneToManyAssociation.class, (spec, collectionId)->
-        spec.getAssociation(collectionId)
-        .map(collection->collection.isOneToManyAssociation()?collection:null)),
-        
-        ACTION(ObjectAction.class, (spec, actionId)->
-        spec.getObjectAction(actionId));
-        
-        @Getter private final Class<? extends ObjectMember> memberType;
-        @Getter private final BiFunction<
-                ObjectSpecification, String, 
-                Optional<? extends ObjectMember>
-            > memberProvider;
-        
-    }
-    
-    public static enum AccessIntent {
-        ACCESS, MUTATE;
-
-        public boolean isMutate() {
-            return this == MUTATE;
-        }
-    }
-
     @Getter
     private final ManagedObject managedObject;
     
@@ -81,319 +33,49 @@ public class ObjectBinding {
         return managedObject.getSpecification().getTitle(null, managedObject);
     }
 
-    @Deprecated
-    public Stream<OneToOneAssociation> streamVisisbleProperties() {
-        return managedObject.getSpecification()
-        .streamAssociations(Contributed.INCLUDED)
-        .filter(objMember->objMember.getFeatureType().isProperty())
-        //TODO filter visibility
-        .map(OneToOneAssociation.class::cast);
-    }
-    
-    @Deprecated
-    public Stream<OneToManyAssociation> streamVisisbleCollections() {
-        return managedObject.getSpecification()
-        .streamAssociations(Contributed.INCLUDED)
-        .filter(objMember->objMember.getFeatureType().isCollection())
-        //TODO filter visibility
-        .map(OneToManyAssociation.class::cast);
-    }
-    
-    @Deprecated
-    public Stream<ObjectAction> streamVisisbleActions() {
-        return managedObject.getSpecification()
-        .streamObjectActions(Contributed.INCLUDED)
-        .filter(objMember->objMember.getFeatureType().isAction())
-        //TODO filter visibility
-        .map(ObjectAction.class::cast);
-    }
-    
-    public Optional<ObjectAction> lookupAction(String actionId) {
-        return streamVisisbleActions()
-                .filter(action->Objects.equals(actionId, action.getId()))
-                .findFirst();
-    }
-    
-    public Optional<OneToOneAssociation> lookupVisibleProperty(String 
propertyId) {
-        return streamVisisbleProperties()
-                .filter(property->Objects.equals(propertyId, property.getId()))
-                .findFirst();
-    }
-
-    public Optional<OneToManyAssociation> lookupCollection(String 
collectionId) {
-        return streamVisisbleCollections()
-                .filter(collection->Objects.equals(collectionId, 
collection.getId()))
-                .findFirst();
-    }
-
-    /**
-     * @param actionId
-     * @param where
-     * @return ActionAccessChain with either an editable or readonly 
ActionBinding, 
-     * based on visibility and usability
-     */
-    public ActionAccessChain getActionBinding(String actionId, Where where) {
-        val either = getActionThatIsVisible(actionId, where)
-        .map(
-                action->
-                    checkUsability(action, where).isSuccess()
-                       ? ActionBindingUsable.of(managedObject, action)
-                       : ActionBindingReadonly.of(managedObject, action), 
-                UnaryOperator.identity());
-        return ActionAccessChain.of(either);
-    }
-    
-    /**
-     * 
-     * @param actionId
-     * @param where
-     * @param accessIntent
-     * @return ActionAccessChain with either an editable or readonly 
PropertyBinding, 
-     * based on visibility and accessIntent
-     */
-    public ActionAccessChain getActionBinding(
-            final String actionId, 
-            final Where where, 
-            final AccessIntent accessIntent) {
-        
-        val either = getActionThatIsVisibleForIntent(actionId, where, 
accessIntent)
-        .map(
-                action->{
-                    switch(accessIntent) {
-                    case ACCESS:
-                        return ActionBindingReadonly.of(managedObject, action);
-                    case MUTATE:
-                        return ActionBindingUsable.of(managedObject, action);
-                    default:
-                        throw _Exceptions.unmatchedCase(accessIntent);
-                    }
-                },
-                UnaryOperator.identity());
-        return ActionAccessChain.of(either);
-    }
-    
-    /**
-     * @param propertyId
-     * @param where
-     * @return PropertyAccessChain with either an editable or readonly 
PropertyBinding, 
-     * based on visibility and usability
-     */
-    public PropertyAccessChain getPropertyBinding(String propertyId, Where 
where) {
-        val either = getPropertyThatIsVisible(propertyId, where)
-        .map(
-                property->
-                    checkUsability(property, where).isSuccess()
-                       ? PropertyBindingEditable.of(managedObject, property)
-                       : PropertyBindingReadonly.of(managedObject, property), 
-                UnaryOperator.identity());
-        return PropertyAccessChain.of(either);
-    }
-    
-    /**
-     * 
-     * @param propertyId
-     * @param where
-     * @param accessIntent
-     * @return PropertyAccessChain with either an editable or readonly 
PropertyBinding, 
-     * based on visibility and accessIntent
-     */
-    public PropertyAccessChain getPropertyBinding(
-            final String propertyId, 
-            final Where where, 
-            final AccessIntent accessIntent) {
-        
-        val either = getPropertyThatIsVisibleForIntent(propertyId, where, 
accessIntent)
-        .map(
-                property->{
-                    switch(accessIntent) {
-                    case ACCESS:
-                        return PropertyBindingReadonly.of(managedObject, 
property);
-                    case MUTATE:
-                        return PropertyBindingEditable.of(managedObject, 
property);
-                    default:
-                        throw _Exceptions.unmatchedCase(accessIntent);
-                    }
-                },
-                UnaryOperator.identity());
-        return PropertyAccessChain.of(either);
-    }
-    
-    /**
-     * @param collectionId
-     * @param where
-     * @return CollectionAccessChain with either an editable or readonly 
CollectionBinding, 
-     * based on visibility and usability
-     */
-    public CollectionAccessChain getCollectionBinding(String collectionId, 
Where where) {
-        val either = getCollectionThatIsVisible(collectionId, where)
-        .map(
-                coll->
-                    checkUsability(coll, where).isSuccess()
-                       ? CollectionBindingEditable.of(managedObject, coll)
-                       : CollectionBindingReadonly.of(managedObject, coll), 
-                UnaryOperator.identity());
-        
-        return CollectionAccessChain.of(either);
-    }
-    
-    /**
-     * 
-     * @param collectionId
-     * @param where
-     * @param accessIntent
-     * @return CollectionAccessChain with either an editable or readonly 
CollectionBinding, 
-     * based on visibility and accessIntent
-     */
-    public CollectionAccessChain getCollectionBinding(
-            final String collectionId, 
-            final Where where, 
-            final AccessIntent accessIntent) {
-        
-        val either = getCollectionThatIsVisibleForIntent(collectionId, where, 
accessIntent)
-        .map(
-                coll->{
-                    switch(accessIntent) {
-                    case ACCESS:
-                        return CollectionBindingReadonly.of(managedObject, 
coll);
-                    case MUTATE:
-                        return CollectionBindingEditable.of(managedObject, 
coll);
-                    default:
-                        throw _Exceptions.unmatchedCase(accessIntent);
-                    }
-                },
-                UnaryOperator.identity());
-        
-        return CollectionAccessChain.of(either);
-
-    }
-    
-    // -- HELPER - TOP LEVEL
-
-    private _Either<ObjectAction, InteractionResponse> 
-    getActionThatIsVisible(String actionId, Where where) {
-        val chain = this.<ObjectAction>startChain(MemberType.ACTION, actionId);
-        return checkVisibility(chain, MemberType.ACTION, where);
-    }
-    
-    private _Either<OneToOneAssociation, InteractionResponse> 
-    getPropertyThatIsVisible(String propertyId, Where where) {
-        val chain = this.<OneToOneAssociation>startChain(MemberType.PROPERTY, 
propertyId);
-        return checkVisibility(chain, MemberType.PROPERTY, where);
-    }
-    
-    private _Either<OneToManyAssociation, InteractionResponse> 
-    getCollectionThatIsVisible(String collectionId, Where where) {
-        val chain = 
this.<OneToManyAssociation>startChain(MemberType.COLLECTION, collectionId);
-        return checkVisibility(chain, MemberType.COLLECTION, where);
-    }
-    
-    private _Either<ObjectAction, InteractionResponse> 
getActionThatIsVisibleForIntent(
-            final String actionId, 
-            final Where where, 
-            final AccessIntent accessIntent) {
-        return checkUsability(getActionThatIsVisible(actionId, where), where, 
accessIntent);
-    }
-    
-    private _Either<OneToOneAssociation, InteractionResponse> 
getPropertyThatIsVisibleForIntent(
-            final String propertyId, 
-            final Where where, 
-            final AccessIntent accessIntent) {
-        return checkUsability(getPropertyThatIsVisible(propertyId, where), 
where, accessIntent);
-    }
-    
-    private _Either<OneToManyAssociation, InteractionResponse> 
getCollectionThatIsVisibleForIntent(
-            final String propertyId, 
-            final Where where, 
-            final AccessIntent accessIntent) {
-        return checkUsability(getCollectionThatIsVisible(propertyId, where), 
where, accessIntent);
-    }
-    
-    // HELPER - MID LEVEL
-    
-    private <T extends ObjectMember> _Either<T, InteractionResponse> 
startChain(
-            final MemberType memberType,
-            final String memberId) {
-        
-        val spec = managedObject.getSpecification();
-        val member = memberType.getMemberProvider().apply(spec, memberId);
-        
-        if(!member.isPresent()) {
-            return _Either.right(InteractionResponse.failed(
-                    Veto.NOT_FOUND,
-                    notFound(memberType, memberId)));
-        }
-        return _Either.<T, InteractionResponse>left((T)member.get());
-    }
-    
-    private <T extends ObjectMember> _Either<T, InteractionResponse> 
checkVisibility(
-            final _Either<T, InteractionResponse> chain,
-            final MemberType memberType,
-            final Where where) {
-        
-        return chain.leftRemap(objectMember->{
-            val visibility = checkVisibility(memberType, objectMember, where);
-            if(visibility.isFailure()) {
-                return _Either.right(visibility);
-            }
-            return _Either.left(objectMember);
-            
-        });
-    }
-    
-    private <T extends ObjectMember> _Either<T, InteractionResponse> 
checkUsability(
-            final _Either<T, InteractionResponse> chain, 
-            final Where where, 
-            final AccessIntent accessIntent) {
-        
-        return chain.leftRemap(objectMember->{
-            if(accessIntent.isMutate()) {
-                val usability = checkUsability(objectMember, where);
-                if(usability.isFailure()) {
-                    return _Either.right(usability);
-                }
-            }
-            return _Either.left(objectMember);
-            
-        });
-    }
-
-    // HELPER - LOWEST LEVEL
-    
-    private <T extends ObjectMember> InteractionResponse checkVisibility(
-            final MemberType memberType,
-            final T objectMember, 
-            final Where where) {
-
-        val visibilityConsent =
-                objectMember.isVisible(
-                        managedObject, InteractionInitiatedBy.USER, where);
-        return visibilityConsent.isVetoed()
-                ? InteractionResponse.failed(
-                    Veto.HIDDEN,
-                    notFound(memberType, objectMember.getId()))
-                : InteractionResponse.success();
-    }
-    
-    private <T extends ObjectMember> InteractionResponse checkUsability(
-            final T objectMember, 
-            final Where where) {
-        
-        val usabilityConsent = 
-                objectMember.isUsable(
-                    managedObject, InteractionInitiatedBy.USER, where);
-        return usabilityConsent.isVetoed()
-                ? InteractionResponse.failed(
-                    Veto.FORBIDDEN,
-                    usabilityConsent.getReason())
-                : InteractionResponse.success();
-    }
-   
-    private <T extends ObjectMember> String notFound(
-            MemberType memberType, 
-            String memberId) {
-        return String.format("%s '%s' either does not exist, is disabled or is 
not visible", 
-                        memberId, 
-                        memberType.name().toLowerCase());
-    }
+//    @Deprecated
+//    public Stream<OneToOneAssociation> streamVisisbleProperties() {
+//        return managedObject.getSpecification()
+//        .streamAssociations(Contributed.INCLUDED)
+//        .filter(objMember->objMember.getFeatureType().isProperty())
+//        //TODO filter visibility
+//        .map(OneToOneAssociation.class::cast);
+//    }
+//    
+//    @Deprecated
+//    public Stream<OneToManyAssociation> streamVisisbleCollections() {
+//        return managedObject.getSpecification()
+//        .streamAssociations(Contributed.INCLUDED)
+//        .filter(objMember->objMember.getFeatureType().isCollection())
+//        //TODO filter visibility
+//        .map(OneToManyAssociation.class::cast);
+//    }
+//    
+//    @Deprecated
+//    public Stream<ObjectAction> streamVisisbleActions() {
+//        return managedObject.getSpecification()
+//        .streamObjectActions(Contributed.INCLUDED)
+//        .filter(objMember->objMember.getFeatureType().isAction())
+//        //TODO filter visibility
+//        .map(ObjectAction.class::cast);
+//    }
+//    
+//    public Optional<ObjectAction> lookupAction(String actionId) {
+//        return streamVisisbleActions()
+//                .filter(action->Objects.equals(actionId, action.getId()))
+//                .findFirst();
+//    }
+//    
+//    public Optional<OneToOneAssociation> lookupVisibleProperty(String 
propertyId) {
+//        return streamVisisbleProperties()
+//                .filter(property->Objects.equals(propertyId, 
property.getId()))
+//                .findFirst();
+//    }
+//
+//    public Optional<OneToManyAssociation> lookupCollection(String 
collectionId) {
+//        return streamVisisbleCollections()
+//                .filter(collection->Objects.equals(collectionId, 
collection.getId()))
+//                .findFirst();
+//    }
     
 }
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyAccessChain.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyAccessChain.java
deleted file mode 100644
index 633f632..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyAccessChain.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import org.apache.isis.core.commons.internal.base._Either;
-import org.apache.isis.core.commons.internal.exceptions._Exceptions;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(staticName = "of")
-public class PropertyAccessChain {
-    
-    @NonNull private _Either<PropertyBinding, InteractionResponse> chain;
-
-    public PropertyAccessChain modifyProperty(
-            @NonNull final Function<OneToOneAssociation, ManagedObject> 
newProperyValueProvider) {
-
-        chain = chain.leftRemap(propertyBinding->{
-            final InteractionResponse iResponse = 
propertyBinding.modifyProperty(newProperyValueProvider);
-            if(iResponse.isFailure()) {
-                _Either.right(iResponse);
-            }
-            return _Either.left(propertyBinding);
-        });
-        
-        return this;
-    }
-
-    public PropertyAccessChain onFailure(Consumer<InteractionResponse> 
onFailure) {
-        chain.right().ifPresent(onFailure);
-        return this;
-    }
-
-    public PropertyBinding getBinding() {
-        return chain.left()
-                .orElseThrow(_Exceptions::noSuchElement);
-    }
-    
-    public OneToOneAssociation getProperty() {
-        return chain.left()
-                .map(PropertyBinding::getProperty)
-                .orElseThrow(_Exceptions::noSuchElement);
-    }
-
-    public PropertyAccessChain ifPresent(Consumer<PropertyBinding> 
propertyBindingConsumer) {
-        chain.left().ifPresent(propertyBindingConsumer::accept);
-        return this;
-    }
-
-    
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBinding.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBinding.java
deleted file mode 100644
index 690e8bb..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBinding.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import java.util.Optional;
-import java.util.function.Function;
-
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-
-import lombok.NonNull;
-import lombok.val;
-
-public interface PropertyBinding {
-
-    ManagedObject getManagedObject();
-    OneToOneAssociation getProperty();
-
-    InteractionResponse modifyProperty(ManagedObject newProperyValue);
-    
-    default InteractionResponse modifyProperty(
-            @NonNull final Function<OneToOneAssociation, ManagedObject> 
newProperyValueProvider) {
-        return modifyProperty(newProperyValueProvider.apply(getProperty()));
-    }
-    
-    default ManagedObject getPropertyValue() {
-        val property = getProperty();
-        
-        return Optional.ofNullable(property.get(getManagedObject()))
-        .orElse(ManagedObject.of(property.getSpecification(), null));
-    }
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingEditable.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingEditable.java
deleted file mode 100644
index d7f54e1..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingEditable.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import javax.annotation.Nullable;
-
-import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import 
org.apache.isis.viewer.common.model.binding.interaction.InteractionResponse.Veto;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.val;
-
-@RequiredArgsConstructor(staticName = "of")
-public class PropertyBindingEditable implements PropertyBinding {
-
-    @Getter(onMethod = @__(@Override))
-    private final ManagedObject managedObject;
-    
-    @Getter(onMethod = @__(@Override))
-    private final OneToOneAssociation property;
-
-
-    @Override
-    public InteractionResponse modifyProperty(@Nullable ManagedObject 
proposedNewValue) {
-            
-        val consent = property.isAssociationValid(getManagedObject(), 
proposedNewValue, InteractionInitiatedBy.USER);
-        if (consent.isVetoed()) {
-            return InteractionResponse.failed(Veto.UNAUTHORIZED, 
consent.getReason());
-        }
-        
-        property.set(managedObject, proposedNewValue, 
InteractionInitiatedBy.USER);
-        
-        return InteractionResponse.success();
-    }
-
-}
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingReadonly.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingReadonly.java
deleted file mode 100644
index 3c8ed80..0000000
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/PropertyBindingReadonly.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.apache.isis.viewer.common.model.binding.interaction;
-
-import javax.annotation.Nullable;
-
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import 
org.apache.isis.viewer.common.model.binding.interaction.InteractionResponse.Veto;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(staticName = "of")
-public class PropertyBindingReadonly implements PropertyBinding {
-
-    @Getter(onMethod = @__(@Override))
-    private final ManagedObject managedObject;
-    
-    @Getter(onMethod = @__(@Override))
-    private final OneToOneAssociation property;
-
-    @Override
-    public InteractionResponse modifyProperty(@Nullable ManagedObject 
newProperyValue) {
-        // if this code is ever reached we have wired up something wrong 
internally
-        return InteractionResponse.failed(Veto.FORBIDDEN);   
-    }
-
-}
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
index 4bd5c3a..eaa94c3 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
@@ -59,8 +59,8 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty;
 import org.apache.isis.core.runtime.iactn.IsisInteractionTracker;
-import org.apache.isis.viewer.common.model.binding.interaction.ObjectBinding;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.Rel;
 import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
@@ -74,6 +74,7 @@ import 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.MemberReprM
 import 
org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationService;
 import org.apache.isis.viewer.restfulobjects.rendering.util.Util;
 import org.apache.isis.viewer.restfulobjects.viewer.context.ResourceContext;
+import 
org.apache.isis.viewer.restfulobjects.viewer.resources.ObjectAdapterAccessHelper.AccessIntent;
 
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
@@ -451,19 +452,16 @@ public class DomainObjectResourceServerside extends 
ResourceAbstract implements
 
         val objectAdapter = getObjectAdapterElseThrowNotFound(domainType, 
instanceId);
         
-        ObjectBinding.bind(objectAdapter)
-        .getPropertyBinding(
-            propertyId,
-            resourceContext.getWhere(),
-            ObjectBinding.AccessIntent.MUTATE)
+        ManagedProperty.getPropertyHandle(objectAdapter, propertyId)
+        .checkVisibility(resourceContext.getWhere())
+        .checkUsability(resourceContext.getWhere()) // only when 
ObjectBinding.AccessIntent.MUTATE
         .modifyProperty(property->{
             val proposedNewValue = new JsonParserHelper(resourceContext, 
property.getSpecification())
                     .parseAsMapWithSingleValue(Util.asStringUtf8(body));
             
             return proposedNewValue;
         })
-        .onFailure(InteractionFailureHandler::onFailure)
-        ;
+        .getOrElseThrow(InteractionFailureHandler::onFailure);
 
         val domainResourceHelper = 
DomainResourceHelper.ofObjectResource(resourceContext, objectAdapter);
         return domainResourceHelper.propertyDetails(
@@ -489,13 +487,11 @@ public class DomainObjectResourceServerside extends 
ResourceAbstract implements
         
         val objectAdapter = getObjectAdapterElseThrowNotFound(domainType, 
instanceId);
         
-        ObjectBinding.bind(objectAdapter)
-        .getPropertyBinding(
-            propertyId,
-            resourceContext.getWhere(),
-            ObjectBinding.AccessIntent.MUTATE)
+        ManagedProperty.getPropertyHandle(objectAdapter, propertyId)
+        .checkVisibility(resourceContext.getWhere())
+        .checkUsability(resourceContext.getWhere()) // only when 
ObjectBinding.AccessIntent.MUTATE
         .modifyProperty(property->null)
-        .onFailure(InteractionFailureHandler::onFailure);
+        .getOrElseThrow(InteractionFailureHandler::onFailure);
 
         val domainResourceHelper = 
DomainResourceHelper.ofObjectResource(resourceContext, objectAdapter);
         return domainResourceHelper.propertyDetails(
@@ -551,7 +547,7 @@ public class DomainObjectResourceServerside extends 
ResourceAbstract implements
         final ObjectAdapterAccessHelper accessHelper = 
ObjectAdapterAccessHelper.of(resourceContext, objectAdapter);
 
         final OneToManyAssociation collection = 
accessHelper.getCollectionThatIsVisibleForIntent(
-                collectionId, ObjectBinding.AccessIntent.MUTATE);
+                collectionId, AccessIntent.MUTATE);
 
         if (!collection.getCollectionSemantics().isAnySet()) {
             throw 
RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST,
 "Collection '%s' does not have set semantics", collectionId);
@@ -590,7 +586,7 @@ public class DomainObjectResourceServerside extends 
ResourceAbstract implements
         final ObjectAdapterAccessHelper accessHelper = 
ObjectAdapterAccessHelper.of(resourceContext, objectAdapter);
 
         final OneToManyAssociation collection = 
accessHelper.getCollectionThatIsVisibleForIntent(
-                collectionId, ObjectBinding.AccessIntent.MUTATE);
+                collectionId, AccessIntent.MUTATE);
 
         if (!collection.getCollectionSemantics().isListOrArray()) {
             throw 
RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED,
 "Collection '%s' does not have list or array semantics", collectionId);
@@ -629,7 +625,7 @@ public class DomainObjectResourceServerside extends 
ResourceAbstract implements
         final ObjectAdapterAccessHelper accessHelper = 
ObjectAdapterAccessHelper.of(resourceContext, objectAdapter);
 
         final OneToManyAssociation collection = 
accessHelper.getCollectionThatIsVisibleForIntent(
-                collectionId, ObjectBinding.AccessIntent.MUTATE);
+                collectionId, AccessIntent.MUTATE);
 
         final ObjectSpecification collectionSpec = 
collection.getSpecification();
         final ManagedObject argAdapter = new JsonParserHelper(resourceContext, 
collectionSpec)
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java
index 56dee24..9315f65 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainResourceHelper.java
@@ -27,7 +27,6 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import org.apache.isis.viewer.common.model.binding.interaction.ObjectBinding;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.RestfulResponse;
 import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext;
@@ -43,6 +42,7 @@ import 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndCo
 import 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndProperty2;
 import 
org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationService;
 import org.apache.isis.viewer.restfulobjects.viewer.context.ResourceContext;
+import 
org.apache.isis.viewer.restfulobjects.viewer.resources.ObjectAdapterAccessHelper.AccessIntent;
 
 import lombok.val;
 
@@ -109,7 +109,7 @@ class DomainResourceHelper {
 
         ObjectAdapterAccessHelper accessHelper = 
ObjectAdapterAccessHelper.of(resourceContext, objectAdapter);
 
-        final OneToOneAssociation property = 
accessHelper.getPropertyThatIsVisibleForIntent(propertyId, 
ObjectBinding.AccessIntent.ACCESS);
+        final OneToOneAssociation property = 
accessHelper.getPropertyThatIsVisibleForIntent(propertyId, AccessIntent.ACCESS);
 
         transactionService.flushTransaction();
         return representationService.propertyDetails(resourceContext, new 
ObjectAndProperty2(objectAdapter, property, memberMode), memberMode);
@@ -127,7 +127,7 @@ class DomainResourceHelper {
 
         ObjectAdapterAccessHelper accessHelper = 
ObjectAdapterAccessHelper.of(resourceContext, objectAdapter);
 
-        final OneToManyAssociation collection = 
accessHelper.getCollectionThatIsVisibleForIntent(collectionId, 
ObjectBinding.AccessIntent.ACCESS);
+        final OneToManyAssociation collection = 
accessHelper.getCollectionThatIsVisibleForIntent(collectionId, 
AccessIntent.ACCESS);
 
         transactionService.flushTransaction();
         return representationService.collectionDetails(resourceContext, new 
ObjectAndCollection2(objectAdapter, collection, memberMode), memberMode);
@@ -143,7 +143,7 @@ class DomainResourceHelper {
 
         ObjectAdapterAccessHelper accessHelper = 
ObjectAdapterAccessHelper.of(resourceContext, objectAdapter);
 
-        final ObjectAction action = 
accessHelper.getObjectActionThatIsVisibleForIntent(actionId, 
ObjectBinding.AccessIntent.ACCESS);
+        final ObjectAction action = 
accessHelper.getObjectActionThatIsVisibleForIntent(actionId, 
AccessIntent.ACCESS);
 
         transactionService.flushTransaction();
         return representationService.actionPrompt(resourceContext, new 
ObjectAndAction(objectAdapter, action));
@@ -163,7 +163,7 @@ class DomainResourceHelper {
 
         final ObjectAdapterAccessHelper accessHelper = 
ObjectAdapterAccessHelper.of(resourceContext, objectAdapter);
 
-        final ObjectAction action = 
accessHelper.getObjectActionThatIsVisibleForIntent(actionId, 
ObjectBinding.AccessIntent.MUTATE);
+        final ObjectAction action = 
accessHelper.getObjectActionThatIsVisibleForIntent(actionId, 
AccessIntent.MUTATE);
 
         final SemanticsOf actionSemantics = action.getSemantics();
         if (! actionSemantics.isSafeInNature()) {
@@ -187,7 +187,7 @@ class DomainResourceHelper {
 
         final ObjectAdapterAccessHelper accessHelper = 
ObjectAdapterAccessHelper.of(resourceContext, objectAdapter);
 
-        final ObjectAction action = 
accessHelper.getObjectActionThatIsVisibleForIntent(actionId, 
ObjectBinding.AccessIntent.MUTATE);
+        final ObjectAction action = 
accessHelper.getObjectActionThatIsVisibleForIntent(actionId, 
AccessIntent.MUTATE);
 
         final SemanticsOf actionSemantics = action.getSemantics();
         if (!actionSemantics.isIdempotentInNature()) {
@@ -205,7 +205,7 @@ class DomainResourceHelper {
 
         ObjectAdapterAccessHelper accessHelper = 
ObjectAdapterAccessHelper.of(resourceContext, objectAdapter);
 
-        final ObjectAction action = 
accessHelper.getObjectActionThatIsVisibleForIntent(actionId, 
ObjectBinding.AccessIntent.MUTATE);
+        final ObjectAction action = 
accessHelper.getObjectActionThatIsVisibleForIntent(actionId, 
AccessIntent.MUTATE);
 
         return invokeActionUsingAdapters(action, arguments, 
ActionResultReprRenderer.SelfLink.EXCLUDED);
     }
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java
index 4251651..426fbbc 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/InteractionFailureHandler.java
@@ -18,30 +18,64 @@
  */
 package org.apache.isis.viewer.restfulobjects.viewer.resources;
 
-import 
org.apache.isis.viewer.common.model.binding.interaction.InteractionResponse;
+import javax.annotation.Nullable;
+
+import org.apache.isis.core.metamodel.spec.interaction.InteractionVeto;
 import org.apache.isis.viewer.restfulobjects.applib.RestfulResponse;
 import 
org.apache.isis.viewer.restfulobjects.rendering.RestfulObjectsApplicationException;
 
 public class InteractionFailureHandler {
 
-    public static void onFailure(final InteractionResponse failure) {
+    public static RestfulObjectsApplicationException onFailure(@Nullable final 
InteractionVeto failure) {
         
-        if(failure==null || failure.isSuccess()) {
-            return;
+        if(failure==null) {
+            return RestfulObjectsApplicationException
+                    
.createWithMessage(RestfulResponse.HttpStatusCode.INTERNAL_SERVER_ERROR,
+                            "unexpected empty failure holder");
         }
         
-        switch(failure.getVeto()) {
+        switch(failure.getVetoType()) {
         case NOT_FOUND:
         case HIDDEN:
-            throw RestfulObjectsApplicationException
+            return RestfulObjectsApplicationException
             .createWithMessage(RestfulResponse.HttpStatusCode.NOT_FOUND,
-                    failure.getFailureMessage());
-        case UNAUTHORIZED:
-        case FORBIDDEN:
-            throw RestfulObjectsApplicationException
+                    failure.getReason());
+        case READONLY:
+        case INVALID:
+            return RestfulObjectsApplicationException
             .createWithMessage(RestfulResponse.HttpStatusCode.FORBIDDEN,
-                    failure.getFailureMessage());
+                    failure.getReason());
         }
+        
+        return RestfulObjectsApplicationException
+                
.createWithMessage(RestfulResponse.HttpStatusCode.INTERNAL_SERVER_ERROR,
+                        "unmatched veto type " + failure.getVetoType());
+        
     }
     
+    
+//    public static void onFailure(@Nullable final InteractionVeto failure) {
+//        
+//        if(failure==null) {
+//            return;
+//        }
+//        
+//        switch(failure.getVetoType()) {
+//        case NOT_FOUND:
+//        case HIDDEN:
+//            throw RestfulObjectsApplicationException
+//            .createWithMessage(RestfulResponse.HttpStatusCode.NOT_FOUND,
+//                    failure.getReason());
+//        case READONLY:
+//        case INVALID:
+//            throw RestfulObjectsApplicationException
+//            .createWithMessage(RestfulResponse.HttpStatusCode.FORBIDDEN,
+//                    failure.getReason());
+//        }
+//        
+//    }
+        
+        
+
+    
 }
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectAdapterAccessHelper.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectAdapterAccessHelper.java
index fee1000..0bdf621 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectAdapterAccessHelper.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/ObjectAdapterAccessHelper.java
@@ -23,11 +23,13 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import org.apache.isis.viewer.common.model.binding.interaction.ObjectBinding;
-import 
org.apache.isis.viewer.common.model.binding.interaction.ObjectBinding.AccessIntent;
+import org.apache.isis.core.metamodel.spec.interaction.ManagedAction;
+import org.apache.isis.core.metamodel.spec.interaction.ManagedCollection;
+import org.apache.isis.core.metamodel.spec.interaction.ManagedProperty;
 import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext;
 
 import lombok.RequiredArgsConstructor;
+import lombok.val;
 
 /**
  * Utility class that encapsulates the logic for checking access to the 
specified
@@ -35,40 +37,73 @@ import lombok.RequiredArgsConstructor;
  */
 @RequiredArgsConstructor
 public class ObjectAdapterAccessHelper {
+    
+    public static enum AccessIntent {
+        ACCESS, MUTATE;
+
+        public boolean isMutate() {
+            return this == MUTATE;
+        }
+    }
 
     public static ObjectAdapterAccessHelper of(
             final IResourceContext resourceContext,
             final ManagedObject managedObject) {
         return new ObjectAdapterAccessHelper(
-                ObjectBinding.bind(managedObject),
+                managedObject,
                 resourceContext.getWhere());
     }
 
-    private final ObjectBinding objectInteractor;
+    private final ManagedObject managedObject;
     private final Where where;
+    
+    public ObjectAction getObjectActionThatIsVisibleForIntent(
+            final String actionId, final AccessIntent intent) {
+        
+        val actionHandle = ManagedAction
+                .getActionHandle(managedObject, actionId)
+                .checkVisibility(where);
+        
+        if(intent.isMutate()) {
+            actionHandle.checkUsability(where);
+        }
+        
+        return actionHandle
+                .getOrElseThrow(InteractionFailureHandler::onFailure)
+                .getAction();
+    }
 
     public OneToOneAssociation getPropertyThatIsVisibleForIntent(
             final String propertyId, final AccessIntent intent) {
-
-        return objectInteractor.getPropertyBinding(propertyId, where, intent)
-            .onFailure(InteractionFailureHandler::onFailure)
-            .getProperty();
+        
+        val propertyHandle = ManagedProperty
+                .getPropertyHandle(managedObject, propertyId)
+                .checkVisibility(where);
+        
+        if(intent.isMutate()) {
+            propertyHandle.checkUsability(where);
+        }
+        
+        return propertyHandle
+                .getOrElseThrow(InteractionFailureHandler::onFailure)
+                .getProperty();
+        
     }
 
     public OneToManyAssociation getCollectionThatIsVisibleForIntent(
             final String collectionId, final AccessIntent intent) {
         
-        return objectInteractor.getCollectionBinding(collectionId, where, 
intent)
-            .onFailure(InteractionFailureHandler::onFailure)
-            .getCollection();
-    }
-
-    public ObjectAction getObjectActionThatIsVisibleForIntent(
-            final String actionId, final AccessIntent intent) {
+        val collectionHandle = ManagedCollection
+                .getCollectionHandle(managedObject, collectionId)
+                .checkVisibility(where);
+        
+        if(intent.isMutate()) {
+            collectionHandle.checkUsability(where);
+        }
         
-        return objectInteractor.getActionBinding(actionId, where, intent)
-            .onFailure(InteractionFailureHandler::onFailure)
-            .getAction();
+        return collectionHandle
+                .getOrElseThrow(InteractionFailureHandler::onFailure)
+                .getCollection();
     }
 
 

Reply via email to