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/06/09 13:40:30 UTC

[isis] branch master updated: ISIS-3008: optimization: refresh viewmodels only once per interaction

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 5db723fc2b ISIS-3008: optimization: refresh viewmodels only once per interaction
5db723fc2b is described below

commit 5db723fc2be4acce886c62295f1e4d17c84b8fdf
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Jun 9 15:40:24 2022 +0200

    ISIS-3008: optimization: refresh viewmodels only once per interaction
---
 .../isis/core/metamodel/spec/ManagedObject.java    | 134 ++++++++++++++-------
 .../isis/core/metamodel/spec/ManagedObjects.java   |  44 ++-----
 .../core/metamodel/spec/PackedManagedObject.java   |  20 ++-
 .../components/scalars/ScalarPanelAbstract2.java   |   4 +-
 4 files changed, 109 insertions(+), 93 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObject.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObject.java
index 87ef6931cf..980ab25032 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObject.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObject.java
@@ -18,7 +18,9 @@
  */
 package org.apache.isis.core.metamodel.spec;
 
+import java.util.Objects;
 import java.util.Optional;
+import java.util.UUID;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.UnaryOperator;
@@ -35,6 +37,7 @@ import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.facets.object.icon.ObjectIcon;
 import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest;
+import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
 import org.apache.isis.core.metamodel.spec.feature.ObjectFeature;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
@@ -65,15 +68,6 @@ public interface ManagedObject {
      */
     Object getPojo();
 
-    /**
-     * Introduced, so we can re-fetch detached entity pojos in place.
-     * @apiNote should be package private, and not publicly exposed
-     * (but the <i>Java</i> language is not there yet)
-     */
-    void replacePojo(UnaryOperator<Object> replacer);
-
-    void replaceBookmark(UnaryOperator<Bookmark> replacer);
-
     /**
      * Returns the object's bookmark as identified by the ObjectManager.
      * Bookmarks are considered immutable, hence will be memoized once fetched.
@@ -91,39 +85,11 @@ public interface ManagedObject {
     Optional<Bookmark> getBookmarkRefreshed();
 
     /**
-     * Reload current viewmodel object from memoized bookmark, otherwise does nothing.
+     * If the underlying domain object is a viewmodel, refreshes any referenced entities.
+     * (Acts as a no-op otherwise.)
+     * @apiNote usually should be sufficient to refresh once per interaction.
      */
-    default void reloadViewmodelFromMemoizedBookmark() {
-        val spec = getSpecification();
-        if(isBookmarkMemoized()
-                && spec.isViewModel()) {
-
-            val bookmark = getBookmark().get();
-            val viewModelClass = spec.getCorrespondingClass();
-
-            val recreatedViewmodel =
-                    getMetaModelContext().getFactoryService().viewModel(viewModelClass, bookmark);
-
-            _XrayEvent.event("Viewmodel '%s' recreated from memoized bookmark.", viewModelClass.getName());
-
-            replacePojo(old->recreatedViewmodel);
-        }
-    }
-
-    default void reloadViewmodelFromBookmark(final @NonNull Bookmark bookmark) {
-        val spec = getSpecification();
-        if(spec.isViewModel()) {
-            val viewModelClass = spec.getCorrespondingClass();
-
-            val recreatedViewmodel =
-                    getMetaModelContext().getFactoryService().viewModel(viewModelClass, bookmark);
-
-            _XrayEvent.event("Viewmodel '%s' recreated from provided bookmark.", viewModelClass.getName());
-
-            replacePojo(old->recreatedViewmodel);
-            replaceBookmark(old->bookmark);
-        }
-    }
+    void refreshViewmodel(@Nullable Supplier<Bookmark> bookmarkSupplier);
 
     boolean isBookmarkMemoized();
 
@@ -341,8 +307,7 @@ public interface ManagedObject {
             return getBookmark();
         }
 
-        @Override
-        public final void replaceBookmark(final UnaryOperator<Bookmark> replacer) {
+        private void replaceBookmark(final UnaryOperator<Bookmark> replacer) {
             final Bookmark old = bookmarkLazy.isMemoized()
                     ? bookmarkLazy.get().orElse(null)
                     : null;
@@ -367,6 +332,89 @@ public interface ManagedObject {
                     .map(objectManager->objectManager.bookmarkObject(adapter));
         }
 
+        // -- REFRESH OPTIMIZATION
+
+        private UUID interactionId = null;
+
+        @Override
+        public final void refreshViewmodel(final @Nullable Supplier<Bookmark> bookmarkSupplier) {
+            val spec = getSpecification();
+            if(spec.isViewModel()) {
+                val viewModelFacet = spec.getFacet(ViewModelFacet.class);
+                if(viewModelFacet.containsEntities()) {
+
+                    val shouldRefresh = spec.getMetaModelContext().getInteractionProvider().getInteractionId()
+                    .map(this::shouldRefresh)
+                    .orElse(true); // if there is no current interaction, refresh regardless; unexpected state, might fail later
+
+                    if(!shouldRefresh) {
+                        return;
+                    }
+
+                    if(isBookmarkMemoized()) {
+                        reloadViewmodelFromMemoizedBookmark();
+                    } else {
+                        val bookmark = bookmarkSupplier!=null
+                                ? bookmarkSupplier.get()
+                                : null;
+                        if(bookmark!=null) {
+                            reloadViewmodelFromBookmark(bookmark);
+                        }
+                    }
+                }
+            }
+        }
+
+        private boolean shouldRefresh(final @NonNull UUID interactionId) {
+            if(Objects.equals(this.interactionId, interactionId)) {
+                return false; // already refreshed within current interaction
+            }
+            this.interactionId = interactionId;
+            return true;
+        }
+
+        /**
+         * Reload current viewmodel object from memoized bookmark, otherwise does nothing.
+         */
+        private void reloadViewmodelFromMemoizedBookmark() {
+            val spec = getSpecification();
+            if(isBookmarkMemoized()
+                    && spec.isViewModel()) {
+
+                val bookmark = getBookmark().get();
+                val viewModelClass = spec.getCorrespondingClass();
+
+                val recreatedViewmodel =
+                        getMetaModelContext().getFactoryService().viewModel(viewModelClass, bookmark);
+
+                _XrayEvent.event("Viewmodel '%s' recreated from memoized bookmark.", viewModelClass.getName());
+
+                replacePojo(old->recreatedViewmodel);
+            }
+        }
+
+        private void reloadViewmodelFromBookmark(final @NonNull Bookmark bookmark) {
+            val spec = getSpecification();
+            if(spec.isViewModel()) {
+                val viewModelClass = spec.getCorrespondingClass();
+
+                val recreatedViewmodel =
+                        getMetaModelContext().getFactoryService().viewModel(viewModelClass, bookmark);
+
+                _XrayEvent.event("Viewmodel '%s' recreated from provided bookmark.", viewModelClass.getName());
+
+                replacePojo(old->recreatedViewmodel);
+                replaceBookmark(old->bookmark);
+            }
+        }
+
+        /**
+         * Introduced, so we can re-fetch detached entity pojos in place.
+         * @apiNote should be package private, and not publicly exposed
+         * (but the <i>Java</i> language is not there yet)
+         */
+        abstract void replacePojo(UnaryOperator<Object> replacer);
+
     }
 
     // -- SIMPLE
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjects.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjects.java
index 74330e565c..9a9da66d0f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjects.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjects.java
@@ -44,13 +44,12 @@ import org.apache.isis.applib.services.repository.EntityState;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.functional.Try;
 import org.apache.isis.commons.internal.assertions._Assert;
+import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.base._Objects;
 import org.apache.isis.commons.internal.collections._Arrays;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Sets;
-import org.apache.isis.commons.internal.debug._Debug;
-import org.apache.isis.commons.internal.debug.xray.XrayUi;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.config.beans.PersistenceStack;
 import org.apache.isis.core.metamodel.commons.CanonicalInvoker;
@@ -59,12 +58,12 @@ import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.facets.collections.CollectionFacet;
 import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest;
-import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
 import org.apache.isis.core.metamodel.interactions.InteractionHead;
 import org.apache.isis.core.metamodel.interactions.InteractionUtils;
 import org.apache.isis.core.metamodel.interactions.ObjectVisibilityContext;
 import org.apache.isis.core.metamodel.interactions.VisibilityContext;
 import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
+import org.apache.isis.core.metamodel.spec.ManagedObject.ManagedObjectWithBookmark;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 
 import lombok.NonNull;
@@ -510,15 +509,8 @@ public final class ManagedObjects {
         }
 
         @Override
-        public void replacePojo(final UnaryOperator<Object> replacer) {
-        }
-
-        @Override
-        public void replaceBookmark(final UnaryOperator<Bookmark> replacer) {
-        }
-
-        @Override
-        public void reloadViewmodelFromMemoizedBookmark() {
+        public void refreshViewmodel(final @Nullable Supplier<Bookmark> bookmarkSupplier) {
+            // noop; only available for viewmodels
         }
     };
 
@@ -727,7 +719,9 @@ public final class ManagedObjects {
             val newState = EntityUtil.getEntityState(reattached);
             _Assert.assertTrue(newState.isAttached());
 
-            managedObject.replacePojo(old->reattached.getPojo());
+            _Casts.castTo(ManagedObjectWithBookmark.class, managedObject)
+            .ifPresent(obj->obj.replacePojo(old->reattached.getPojo()));
+
             return managedObject;
         }
 
@@ -1136,29 +1130,7 @@ public final class ManagedObjects {
         if(isNullOrUnspecifiedOrEmpty(viewmodel)) {
             return; // do nothing
         }
-
-        val spec = viewmodel.getSpecification();
-        if(spec.isViewModel()) {
-            val viewModelFacet = spec.getFacet(ViewModelFacet.class);
-            if(viewModelFacet.containsEntities()) {
-
-                _Debug.onCondition(XrayUi.isXrayEnabled(), ()->{
-                    _Debug.log("about to refresh viewmodel ..");
-                });
-
-                if(viewmodel.isBookmarkMemoized()) {
-                    viewmodel.reloadViewmodelFromMemoizedBookmark();
-                } else {
-                    val bookmark = bookmarkSupplier!=null
-                            ? bookmarkSupplier.get()
-                            : null;
-                    if(bookmark!=null) {
-                        viewmodel.reloadViewmodelFromBookmark(bookmark);
-                    }
-                }
-            }
-        }
+        viewmodel.refreshViewmodel(bookmarkSupplier);
     }
 
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/PackedManagedObject.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/PackedManagedObject.java
index 2377970dcb..a64ad4baf7 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/PackedManagedObject.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/PackedManagedObject.java
@@ -20,13 +20,14 @@ package org.apache.isis.core.metamodel.spec;
 
 import java.util.Collections;
 import java.util.Optional;
-import java.util.function.UnaryOperator;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
+import org.springframework.lang.Nullable;
+
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Lazy;
-import org.apache.isis.commons.internal.exceptions._Exceptions;
 
 import lombok.RequiredArgsConstructor;
 
@@ -55,16 +56,6 @@ public final class PackedManagedObject implements ManagedObject {
                 .collect(Collectors.toList()));
     }
 
-    @Override
-    public void replacePojo(final UnaryOperator<Object> replacer) {
-        throw _Exceptions.unsupportedOperation();
-    }
-
-    @Override
-    public void replaceBookmark(final UnaryOperator<Bookmark> replacer) {
-        throw _Exceptions.unsupportedOperation();
-    }
-
     private final _Lazy<Optional<Bookmark>> bookmarkLazy =
             _Lazy.threadSafe(()->{
                 return Optional.of(getSpecification().getMetaModelContext().getObjectManager().bookmarkObject(this));
@@ -89,4 +80,9 @@ public final class PackedManagedObject implements ManagedObject {
         return nonScalar;
     }
 
+    @Override
+    public void refreshViewmodel(final @Nullable Supplier<Bookmark> bookmarkSupplier) {
+        // noop; only available for viewmodels
+    }
+
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java
index f177cc1e3e..0643f66943 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java
@@ -214,8 +214,8 @@ extends ScalarPanelAbstract {
 
         // add clear-field-button (only if feature is not required and not already cleared)
         val isClearFieldButtonVisible =
-                scalarModel().proposedValue().isPresent()
-                    && !scalarModel().isRequired();
+                !scalarModel().isRequired()
+                    && scalarModel().proposedValue().isPresent();
 
         if(isClearFieldButtonVisible) {
             val clearFieldButton = Wkt.linkAddWithBody(buttonContainer,