You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/10/26 15:23:53 UTC

[isis] branch master updated: ISIS-3263: do the 'removed' entity handling at a more fundamental level (as compared to prev. commit)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new d564656022 ISIS-3263: do the 'removed' entity handling at a more fundamental level (as compared to prev. commit)
d564656022 is described below

commit d564656022c45e52140bbe1116dab7526c1e8dfc
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Oct 26 17:23:46 2022 +0200

    ISIS-3263: do the 'removed' entity handling at a more fundamental level
    (as compared to prev. commit)
    
    - properly model those 3 states an entity adapter can be in (adding the
    REMOVED state)
---
 .../core/metamodel/object/ManagedObjects.java      |  11 --
 .../object/_ManagedObjectEntityHybrid.java         | 115 ++++++++++++++-------
 .../object/_ManagedObjectEntityRemoved.java        |  56 ++++++++++
 3 files changed, 132 insertions(+), 50 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObjects.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObjects.java
index 271dd338b7..270a565654 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObjects.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObjects.java
@@ -68,17 +68,6 @@ public final class ManagedObjects {
         if(adapter instanceof PackedManagedObject) {
             return ((PackedManagedObject)adapter).unpack().isEmpty();
         }
-        // calling getPojo on entities has side-effects, so do null check with special care!
-        if(adapter.getSpecialization().isEntity()
-                && adapter.isBookmarkMemoized()) {
-            // handle the 'deleted' / 'not found' case gracefully
-            try {
-                return adapter.getPojo()==null;
-            } catch (Throwable e) {
-                // if anything goes wrong retrieving the pojo, report as missing
-                return true;
-            }
-        }
         return adapter.getPojo()==null;
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectEntityHybrid.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectEntityHybrid.java
index ac6f3272ac..c27e1fe475 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectEntityHybrid.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectEntityHybrid.java
@@ -20,12 +20,11 @@ package org.apache.causeway.core.metamodel.object;
 
 import java.util.Optional;
 
+import org.apache.causeway.applib.exceptions.unrecoverable.ObjectNotFoundException;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.applib.services.repository.EntityState;
-import org.apache.causeway.commons.functional.Either;
 import org.apache.causeway.commons.internal.assertions._Assert;
 import org.apache.causeway.commons.internal.base._Blackhole;
-import org.apache.causeway.core.metamodel.object.ManagedObject.Specialization;
 
 import lombok.NonNull;
 import lombok.Synchronized;
@@ -42,25 +41,32 @@ extends _ManagedObjectSpecified
 implements _Refetchable {
 
     /**
-     * dynamically mutates from one to the other based on pojo's persistent state;
-     * however, the pojo reference must be kept identical
+     * One of {_ManagedObjectEntityTransient, _ManagedObjectEntityBookmarked, _ManagedObjectEntityRemoved}.
+     * <p>
+     * May dynamically mutate from 'left' to 'right' based on pojo's persistent state.
+     * However, the pojo reference must be kept identical, unless the entity becomes 'removed',
+     * in which case the pojo reference is invalidated and should no longer be accessible to callers.
      */
-    private @NonNull Either<_ManagedObjectEntityTransient, _ManagedObjectEntityBookmarked>
-        eitherDetachedOrBookmarked;
+    private @NonNull ManagedObject variant;
 
     private enum MorphState {
         /** Has no bookmark yet; can be transitioned to BOOKMARKED once
          *  for accompanied pojo, an OID becomes available. */
         TRANSIENT,
-        /** Final state, once we have an OID,
-         * regardless of the accompanied pojo's persistent state. */
-        BOOKMARKED;
+        /** We have an OID,
+         * regardless of the accompanied pojo's persistent state (unless becomes removed). */
+        BOOKMARKED,
+        /** Final state, that can be entered once after we had an OID. */
+        REMOVED;
         public boolean isTransient() { return this == TRANSIENT; }
-//        public boolean isBookmarked() { return this == BOOKMARKED; }
+        public boolean isBookmarked() { return this == BOOKMARKED; }
+        public boolean isRemoved() { return this == REMOVED; }
         static MorphState valueOf(final EntityState entityState) {
-            return entityState.hasOid()
-                    ? BOOKMARKED
-                    : TRANSIENT;
+            return entityState.isRemoved()
+                    ? REMOVED
+                    : entityState.hasOid()
+                        ? BOOKMARKED
+                        : TRANSIENT;
         }
     }
 
@@ -69,14 +75,14 @@ implements _Refetchable {
     _ManagedObjectEntityHybrid(
             final @NonNull _ManagedObjectEntityTransient _transient) {
         super(ManagedObject.Specialization.ENTITY, _transient.getSpecification());
-        this.eitherDetachedOrBookmarked = Either.left(_transient);
+        this.variant = _transient;
         this.morphState = MorphState.TRANSIENT;
     }
 
     _ManagedObjectEntityHybrid(
             final @NonNull _ManagedObjectEntityBookmarked bookmarked) {
         super(ManagedObject.Specialization.ENTITY, bookmarked.getSpecification());
-        this.eitherDetachedOrBookmarked = Either.right(bookmarked);
+        this.variant = bookmarked;
         this.morphState = MorphState.BOOKMARKED;
         _Assert.assertTrue(bookmarked.getBookmark().isPresent(),
                 ()->"bookmarked entity must have bookmark");
@@ -84,28 +90,34 @@ implements _Refetchable {
 
     @Override
     public Optional<Bookmark> getBookmark() {
-        return eitherDetachedOrBookmarked
-                .fold(Bookmarkable::getBookmark, Bookmarkable::getBookmark);
+        return (variant instanceof Bookmarkable)
+                ? ((Bookmarkable)variant).getBookmark()
+                : Optional.empty();
     }
 
     @Override
     public boolean isBookmarkMemoized() {
-        return eitherDetachedOrBookmarked
-                .fold(Bookmarkable::isBookmarkMemoized, Bookmarkable::isBookmarkMemoized);
+        return (variant instanceof Bookmarkable)
+                ? ((Bookmarkable)variant).isBookmarkMemoized()
+                : false;
     }
 
     @Override
     public @NonNull EntityState getEntityState() {
 
-        val entityState = eitherDetachedOrBookmarked
-                .fold(ManagedObject::getEntityState, ManagedObject::getEntityState);
-
+        val entityState = variant.getEntityState();
         val newMorphState = MorphState.valueOf(entityState);
 
+        System.err.printf("%s -> %s%n", peekAtPojo(), entityState);
+
         if(this.morphState!=newMorphState) {
-            log.debug("about to transition to bookmarked variant given {}", entityState);
+            log.debug("about to transition to {} variant given {}", newMorphState.name(), entityState);
             reassessVariant(entityState, peekAtPojo());
-            _Assert.assertTrue(isVariantAttached(), ()->"successful transition");
+            if(newMorphState.isBookmarked()) {
+                _Assert.assertTrue(isVariantBookmarked(), ()->"successful transition");
+            } else if(newMorphState.isRemoved()) {
+                _Assert.assertTrue(isVariantRemoved(), ()->"successful transition");
+            }
             this.morphState = newMorphState;
         }
         return entityState;
@@ -113,18 +125,27 @@ implements _Refetchable {
 
     @Override
     public Object getPojo() {
-        val pojo = eitherDetachedOrBookmarked
-                .fold(ManagedObject::getPojo, ManagedObject::getPojo);
-
-        triggerReassessment();
+        if(isVariantRemoved()) {
+            return null; // don't reassess
+        }
 
-        return pojo;
+        // handle the 'deleted' / 'not found' case gracefully
+        try {
+            val pojo = variant.getPojo();
+            triggerReassessment();
+            return pojo;
+        } catch (ObjectNotFoundException e) {
+            // if object not found, transition to 'removed' state
+            makeRemoved();
+            return null;
+        }
     }
 
     @Override
     public Object peekAtPojo() {
-        return eitherDetachedOrBookmarked
-            .fold(_Refetchable::peekAtPojo, _Refetchable::peekAtPojo);
+        return (variant instanceof _Refetchable)
+                ? ((_Refetchable)variant).peekAtPojo()
+                : null;
     }
 
     @Override
@@ -143,28 +164,44 @@ implements _Refetchable {
         }
     }
 
-    private boolean isVariantAttached() {
-        return eitherDetachedOrBookmarked.isRight();
+    private boolean isVariantBookmarked() {
+        return variant instanceof _ManagedObjectEntityBookmarked;
     }
 
-    private boolean isVariantDetached() {
-        return eitherDetachedOrBookmarked.isLeft();
+    private boolean isVariantTransient() {
+        return variant instanceof _ManagedObjectEntityTransient;
+    }
+
+    private boolean isVariantRemoved() {
+        return variant instanceof _ManagedObjectEntityRemoved;
     }
 
     @Synchronized
     private void reassessVariant(final EntityState entityState, final Object pojo) {
-        if(isVariantDetached()
+        if(isVariantTransient()
                 && entityState.hasOid()) {
-            attach(pojo);
+            makeBookmarked(pojo);
+            return;
+        }
+        if(isVariantBookmarked()
+                && entityState.isRemoved()) {
+            makeRemoved();
+            return;
         }
     }
 
     // morph into attached
-    private void attach(final Object pojo) {
+    private void makeBookmarked(final Object pojo) {
         val attached = new _ManagedObjectEntityBookmarked(getSpecification(), pojo, Optional.empty());
-        eitherDetachedOrBookmarked = Either.right(attached);
+        this.variant = attached;
         _Assert.assertTrue(attached.getBookmark().isPresent(),
                 ()->"bookmarked entity must have bookmark");
     }
 
+    // morph into attached
+    private void makeRemoved() {
+        val removed = new _ManagedObjectEntityRemoved(getSpecification());
+        this.variant = removed;
+    }
+
 }
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectEntityRemoved.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectEntityRemoved.java
new file mode 100644
index 0000000000..ca78e8ca77
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectEntityRemoved.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.core.metamodel.object;
+
+import org.apache.causeway.applib.services.repository.EntityState;
+import org.apache.causeway.commons.internal.assertions._Assert;
+import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+
+import lombok.NonNull;
+
+/**
+ * (package private) specialization corresponding to a removed {@link Specialization#ENTITY}
+ * @see ManagedObject.Specialization#ENTITY
+ */
+final class _ManagedObjectEntityRemoved
+extends _ManagedObjectSpecified
+implements Bookmarkable.NoBookmark {
+
+    _ManagedObjectEntityRemoved(
+            final ObjectSpecification spec) {
+        super(ManagedObject.Specialization.ENTITY, spec);
+        _Assert.assertTrue(spec.isEntity());
+    }
+
+    @Override
+    public String getTitle() {
+        return "deleted entity object";
+    }
+
+    @Override
+    public Object getPojo() {
+        return null;
+    }
+
+    @Override
+    public @NonNull EntityState getEntityState() {
+        return EntityState.PERSISTABLE_REMOVED;
+    }
+
+}
\ No newline at end of file