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/09/05 05:14:14 UTC

[isis] branch master updated: ISIS-3199: be more strict with ObjectLoader, don't allow null requests

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 6ba2da5b99 ISIS-3199: be more strict with ObjectLoader, don't allow null requests
6ba2da5b99 is described below

commit 6ba2da5b99e40fbac987cba2faa4cae0cd8dcc5b
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Sep 5 07:14:08 2022 +0200

    ISIS-3199: be more strict with ObjectLoader, don't allow null requests
---
 .../isis/core/metamodel/object/ProtoObject.java    |  59 ++++++
 .../metamodel/objectmanager/ObjectBookmarker.java  |   2 +-
 .../metamodel/objectmanager/ObjectCreator.java     |   2 +-
 .../core/metamodel/objectmanager/ObjectLoader.java | 154 +++++++++++++++
 .../metamodel/objectmanager/ObjectManager.java     |  18 +-
 .../objectmanager/ObjectManagerDefault.java        |   1 -
 .../metamodel/objectmanager/load/ObjectLoader.java |  71 -------
 .../load/ObjectLoader_builtinHandlers.java         | 220 ---------------------
 .../schema/SchemaValueMarshallerAbstract.java      |  12 +-
 .../command/SchemaValueMarshallerDefault.java      |  19 +-
 .../DelegatingInvocationHandlerDefault.java        |   6 +-
 .../wkt/viewer/FullCalendarWithEventHandling.java  |   9 +-
 12 files changed, 238 insertions(+), 335 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ProtoObject.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ProtoObject.java
new file mode 100644
index 0000000000..bfbed4fbaf
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ProtoObject.java
@@ -0,0 +1,59 @@
+/*
+ *  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.object;
+
+import java.util.Optional;
+
+import org.springframework.lang.Nullable;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+
+import lombok.NonNull;
+
+/**
+ * Pair of {@link ObjectSpecification} and {@link Bookmark}.
+ */
+@lombok.Value(staticConstructor = "of")
+public class ProtoObject {
+
+    private final @NonNull ObjectSpecification objectSpecification;
+    private final @NonNull Bookmark bookmark;
+
+    public static Optional<ProtoObject> resolve(
+            final @NonNull SpecificationLoader specificationLoader,
+            final @Nullable Bookmark bookmark) {
+        if(bookmark==null) {
+            return Optional.empty();
+        }
+        return specificationLoader
+                .specForLogicalTypeName(bookmark.getLogicalTypeName())
+                .map(spec->of(spec, bookmark));
+    }
+
+    public static ProtoObject resolveElseFail(
+            final @NonNull SpecificationLoader specificationLoader,
+            final @NonNull Bookmark bookmark) {
+        return of(specificationLoader
+                    .specForLogicalTypeNameElseFail(bookmark.getLogicalTypeName()),
+                bookmark);
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectBookmarker.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectBookmarker.java
index ad7fcdd096..bc5f22b14f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectBookmarker.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectBookmarker.java
@@ -32,7 +32,7 @@ public interface ObjectBookmarker {
 
     // -- FACTORY
 
-    public static ObjectBookmarker createDefault() {
+    static ObjectBookmarker createDefault() {
         return managedObject ->
             managedObject==null
                     ? Optional.empty()
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectCreator.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectCreator.java
index d36705e147..c62934d863 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectCreator.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectCreator.java
@@ -42,7 +42,7 @@ import lombok.extern.log4j.Log4j2;
  *
  * @since 2.0
  */
-public interface ObjectCreator {
+interface ObjectCreator {
 
     ManagedObject createObject(ObjectSpecification objectSpecification);
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectLoader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectLoader.java
new file mode 100644
index 0000000000..1807916474
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectLoader.java
@@ -0,0 +1,154 @@
+/*
+ *  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.objectmanager;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.handler.ChainOfResponsibility;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.commons.internal.ioc._ManagedBeanAdapter;
+import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.metamodel.facets.object.value.ValueSerializer.Format;
+import org.apache.isis.core.metamodel.object.ManagedObject;
+import org.apache.isis.core.metamodel.object.ProtoObject;
+
+import lombok.NonNull;
+import lombok.val;
+
+/**
+ * @since 2.0
+ */
+public interface ObjectLoader {
+
+    ManagedObject loadObject(@NonNull ProtoObject objectLoadRequest);
+
+    // -- HANDLER
+
+    static interface Handler
+    extends
+        ChainOfResponsibility.Handler<ProtoObject, ManagedObject> {
+    }
+
+    // -- FACTORY
+
+    public static ObjectLoader createDefault(final MetaModelContext mmc) {
+        return request ->
+            ChainOfResponsibility.named(
+                    "ObjectLoader",
+                    handlers)
+                .handle(Objects.requireNonNull(request));
+    }
+
+    // -- HANDLERS
+
+    static final List<Handler> handlers = List.of(BuiltinHandlers.values());
+
+    enum BuiltinHandlers implements Handler {
+        LoadService{
+            @Override
+            public boolean isHandling(final ProtoObject objectLoadRequest) {
+                val spec = objectLoadRequest.getObjectSpecification();
+                return spec.isInjectable();
+            }
+            @Override
+            public ManagedObject handle(final ProtoObject objectLoadRequest) {
+                val spec = objectLoadRequest.getObjectSpecification();
+                val logicalType = spec.getLogicalType();
+                val servicePojo = spec.getServiceRegistry()
+                    .lookupRegisteredBeanById(logicalType)
+                    .map(_ManagedBeanAdapter::getInstance)
+                    .flatMap(Can::getFirst)
+                    .orElseThrow(()->_Exceptions.noSuchElement(
+                            "loader: %s loading logicalType %s",
+                            this.getClass().getName(), logicalType));
+                return ManagedObject.service(spec, servicePojo);
+            }
+        },
+        LoadValue{
+            @Override
+            public boolean isHandling(final ProtoObject objectLoadRequest) {
+                val spec = objectLoadRequest.getObjectSpecification();
+                return spec.isValue();
+            }
+            @Override
+            public ManagedObject handle(final ProtoObject objectLoadRequest) {
+                val spec = objectLoadRequest.getObjectSpecification();
+                val valueFacet = spec.valueFacetElseFail();
+
+                val bookmark = objectLoadRequest.getBookmark();
+                val valuePojoIfAny = valueFacet.destring(Format.URL_SAFE, bookmark.getIdentifier());
+
+                return ManagedObject.value(spec, valuePojoIfAny);
+            }
+        },
+        LoadViewModel{
+            @Override
+            public boolean isHandling(final ProtoObject objectLoadRequest) {
+                val spec = objectLoadRequest.getObjectSpecification();
+                return spec.isViewModel();
+            }
+            @Override
+            public ManagedObject handle(final ProtoObject objectLoadRequest) {
+                val spec = objectLoadRequest.getObjectSpecification();
+                val viewModelFacet = spec.viewmodelFacetElseFail();
+
+                val bookmark = objectLoadRequest.getBookmark();
+                return viewModelFacet.instantiate(spec, Optional.of(bookmark));
+            }
+        },
+        LoadEntity{
+            @Override
+            public boolean isHandling(final ProtoObject objectLoadRequest) {
+                val spec = objectLoadRequest.getObjectSpecification();
+                return spec.isEntity();
+            }
+            @Override
+            public ManagedObject handle(final ProtoObject objectLoadRequest) {
+
+                val spec = objectLoadRequest.getObjectSpecification();
+                val entityFacet = spec.entityFacetElseFail();
+
+                val bookmark = objectLoadRequest.getBookmark();
+                val entityPojoIfAny = entityFacet.fetchByBookmark(bookmark);
+
+                return entityPojoIfAny
+                        .map(entityPojo->ManagedObject.entity(spec, entityPojo, Optional.of(bookmark)))
+                        .orElseGet(()->ManagedObject.empty(spec));
+            }
+        },
+        LoadOther{
+            @Override
+            public boolean isHandling(final ProtoObject objectLoadRequest) {
+                return true; // the last handler in the chain
+            }
+            @Override
+            public ManagedObject handle(final ProtoObject objectLoadRequest) {
+                // unknown object load request
+                 throw _Exceptions.illegalArgument(
+                        "None of the registered ObjectLoaders knows how to load this object. (loader: %s loading %s)",
+                            this.getClass().getName(), objectLoadRequest);
+            }
+        },
+        ;
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java
index d87f17304c..8bde10ba68 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java
@@ -28,9 +28,9 @@ import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.metamodel.context.HasMetaModelContext;
 import org.apache.isis.core.metamodel.object.ManagedObject;
-import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
+import org.apache.isis.core.metamodel.object.ProtoObject;
 import org.apache.isis.core.metamodel.objectmanager.memento.ObjectMemorizer;
 import org.apache.isis.core.metamodel.objectmanager.query.ObjectBulkLoader;
 import org.apache.isis.core.metamodel.objectmanager.refresh.ObjectRefresher;
@@ -49,9 +49,7 @@ import lombok.val;
  *
  * @since 2.0
  */
-public interface ObjectManager {
-
-    MetaModelContext getMetaModelContext();
+public interface ObjectManager extends HasMetaModelContext {
 
     ObjectCreator getObjectCreator();
     ObjectLoader getObjectLoader();
@@ -75,7 +73,7 @@ public interface ObjectManager {
      * Loads an instance identified with given request parameters.
      * @param objectLoadRequest
      */
-    public default ManagedObject loadObject(final ObjectLoader.Request objectLoadRequest) {
+    public default ManagedObject loadObject(final ProtoObject objectLoadRequest) {
         return getObjectLoader().loadObject(objectLoadRequest);
     }
 
@@ -91,11 +89,8 @@ public interface ObjectManager {
             return Optional.empty();
         }
         val specLoader = getMetaModelContext().getSpecificationLoader();
-        val objManager = this;
-        return specLoader
-                .specForLogicalTypeName(bookmark.getLogicalTypeName())
-                .map(spec->objManager.loadObject(
-                        ObjectLoader.Request.of(spec, bookmark)));
+        return ProtoObject.resolve(specLoader, bookmark)
+                .map(this::loadObject);
     }
 
     default ManagedObject loadObjectElseFail(final @NonNull Bookmark bookmark) {
@@ -140,6 +135,7 @@ public interface ObjectManager {
         return specForType(pojo.getClass());
     }
 
+    @Override
     default Optional<ObjectSpecification> specForType(final @Nullable Class<?> domainType) {
         return getMetaModelContext().getSpecificationLoader().specForType(domainType);
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManagerDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManagerDefault.java
index 051117e666..f5852c758f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManagerDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManagerDefault.java
@@ -29,7 +29,6 @@ import org.springframework.stereotype.Service;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.core.metamodel.IsisModuleCoreMetamodel;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
 import org.apache.isis.core.metamodel.objectmanager.memento.ObjectMemorizer;
 import org.apache.isis.core.metamodel.objectmanager.query.ObjectBulkLoader;
 import org.apache.isis.core.metamodel.objectmanager.refresh.ObjectRefresher;
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader.java
deleted file mode 100644
index f87f7957fa..0000000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- *  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.objectmanager.load;
-
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.commons.handler.ChainOfResponsibility;
-import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.core.metamodel.context.HasMetaModelContext;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.object.ManagedObject;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-
-import lombok.Value;
-
-/**
- * @since 2.0
- */
-public interface ObjectLoader {
-
-    ManagedObject loadObject(Request objectLoadRequest);
-
-    // -- REQUEST (VALUE) TYPE
-
-    @Value(staticConstructor = "of")
-    public static class Request {
-        ObjectSpecification objectSpecification;
-        Bookmark bookmark;
-    }
-
-    // -- HANDLER
-
-    static interface Handler
-    extends
-        HasMetaModelContext,
-        ChainOfResponsibility.Handler<ObjectLoader.Request, ManagedObject> {
-
-    }
-
-    // -- FACTORY
-
-    public static ObjectLoader createDefault(final MetaModelContext mmc) {
-        return request ->
-        ChainOfResponsibility.named(
-                "ObjectLoader",
-                _Lists.of(
-                        new ObjectLoader_builtinHandlers.GuardAgainstNull(mmc),
-                        new ObjectLoader_builtinHandlers.LoadService(mmc),
-                        new ObjectLoader_builtinHandlers.LoadValue(mmc),
-                        new ObjectLoader_builtinHandlers.LoadViewModel(mmc),
-                        new ObjectLoader_builtinHandlers.LoadEntity(mmc),
-                        new ObjectLoader_builtinHandlers.LoadOther(mmc)))
-            .handle(request);
-    }
-
-}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
deleted file mode 100644
index 6f4a5ad383..0000000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- *  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.objectmanager.load;
-
-import java.util.Optional;
-
-import org.apache.isis.commons.collections.Can;
-import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.commons.internal.ioc._ManagedBeanAdapter;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facets.object.value.ValueSerializer.Format;
-import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
-import org.apache.isis.core.metamodel.object.ManagedObject;
-
-import lombok.NonNull;
-import lombok.Value;
-import lombok.val;
-
-/**
- *
- * @since 2.0
- *
- */
-final class ObjectLoader_builtinHandlers {
-
-    // -- NULL GUARD
-
-    @Value
-    public static class GuardAgainstNull implements ObjectLoader.Handler {
-
-        private final @NonNull MetaModelContext metaModelContext;
-
-        @Override
-        public boolean isHandling(final ObjectLoader.Request objectLoadRequest) {
-
-            if(objectLoadRequest==null) {
-                return true;
-            }
-
-            val spec = objectLoadRequest.getObjectSpecification();
-            if(spec == null) {
-                // eg "NONEXISTENT:123"
-                return true;
-            }
-
-            // we don't guard against the identifier being null, because, this is ok
-            // for services and values
-            return false;
-        }
-
-        @Override
-        public ManagedObject handle(final ObjectLoader.Request objectLoadRequest) {
-            return null; // yes null
-        }
-
-    }
-
-    // -- SERVICES / MANAGED BEANS
-
-    @Value
-    public static class LoadService implements ObjectLoader.Handler {
-
-        private final @NonNull MetaModelContext metaModelContext;
-
-        @Override
-        public boolean isHandling(final ObjectLoader.Request objectLoadRequest) {
-
-            val spec = objectLoadRequest.getObjectSpecification();
-            return spec.isInjectable();
-        }
-
-        @Override
-        public ManagedObject handle(final ObjectLoader.Request objectLoadRequest) {
-
-            val spec = objectLoadRequest.getObjectSpecification();
-            val logicalType = spec.getLogicalType();
-
-            val servicePojo = metaModelContext.getServiceRegistry()
-                .lookupRegisteredBeanById(logicalType)
-                .map(_ManagedBeanAdapter::getInstance)
-                .flatMap(Can::getFirst)
-                .orElseThrow(()->_Exceptions.noSuchElement(
-                        "loader: %s loading logicalType %s",
-                        this.getClass().getName(), logicalType));
-
-            return ManagedObject.service(spec, servicePojo);
-        }
-
-    }
-
-    // -- VALUES
-
-    @Value
-    public static class LoadValue implements ObjectLoader.Handler {
-
-        private final @NonNull MetaModelContext metaModelContext;
-
-        @Override
-        public boolean isHandling(final ObjectLoader.Request objectLoadRequest) {
-
-            val spec = objectLoadRequest.getObjectSpecification();
-            return spec.isValue();
-        }
-
-        @Override
-        public ManagedObject handle(final ObjectLoader.Request objectLoadRequest) {
-
-            val spec = objectLoadRequest.getObjectSpecification();
-            val valueFacet = spec.valueFacetElseFail();
-
-            val bookmark = objectLoadRequest.getBookmark();
-            val valuePojoIfAny = valueFacet.destring(Format.URL_SAFE, bookmark.getIdentifier());
-
-            return ManagedObject.value(spec, valuePojoIfAny);
-        }
-
-    }
-
-    // -- VIEW MODELS
-
-    @Value
-    public static class LoadViewModel implements ObjectLoader.Handler {
-
-        private final @NonNull MetaModelContext metaModelContext;
-
-        @Override
-        public boolean isHandling(final ObjectLoader.Request objectLoadRequest) {
-
-            val spec = objectLoadRequest.getObjectSpecification();
-            return spec.isViewModel();
-        }
-
-        @Override
-        public ManagedObject handle(final ObjectLoader.Request objectLoadRequest) {
-
-            val spec = objectLoadRequest.getObjectSpecification();
-            val viewModelFacet = spec.getFacet(ViewModelFacet.class);
-            if(viewModelFacet == null) {
-                throw _Exceptions.illegalArgument(
-                        "ObjectSpecification is missing a ViewModelFacet: %s", spec);
-            }
-
-            val bookmark = objectLoadRequest.getBookmark();
-            return viewModelFacet.instantiate(spec, Optional.of(bookmark));
-        }
-
-    }
-
-    // -- ENTITIES
-
-    @Value
-    public static class LoadEntity implements ObjectLoader.Handler {
-
-        private final @NonNull MetaModelContext metaModelContext;
-
-        @Override
-        public boolean isHandling(final ObjectLoader.Request objectLoadRequest) {
-
-            val spec = objectLoadRequest.getObjectSpecification();
-            return spec.isEntity();
-        }
-
-        @Override
-        public ManagedObject handle(final ObjectLoader.Request objectLoadRequest) {
-
-            val spec = objectLoadRequest.getObjectSpecification();
-            val entityFacet = spec.entityFacetElseFail();
-
-            val bookmark = objectLoadRequest.getBookmark();
-            val entityPojoIfAny = entityFacet.fetchByBookmark(bookmark);
-
-            return entityPojoIfAny
-                    .map(entityPojo->ManagedObject.entity(spec, entityPojo, Optional.of(bookmark)))
-                    .orElseGet(()->ManagedObject.empty(spec));
-        }
-
-    }
-
-    // -- UNKNOWN LOAD REQUEST
-
-    @Value
-    public static class LoadOther implements ObjectLoader.Handler {
-
-        private final @NonNull MetaModelContext metaModelContext;
-
-        @Override
-        public boolean isHandling(final ObjectLoader.Request objectLoadRequest) {
-            return true; // the last handler in the chain
-        }
-
-        @Override
-        public ManagedObject handle(final ObjectLoader.Request objectLoadRequest) {
-
-            // unknown object load request
-
-             throw _Exceptions.illegalArgument(
-                    "None of the registered ObjectLoaders knows how to load this object. (loader: %s loading %s)",
-                        this.getClass().getName(), objectLoadRequest);
-
-        }
-
-    }
-
-}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/schema/SchemaValueMarshallerAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/schema/SchemaValueMarshallerAbstract.java
index 6800507f7c..9f02465f3c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/schema/SchemaValueMarshallerAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/schema/SchemaValueMarshallerAbstract.java
@@ -34,16 +34,16 @@ import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.collections.Cardinality;
 import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.core.metamodel.context.HasMetaModelContext;
 import org.apache.isis.core.metamodel.facetapi.FeatureType;
 import org.apache.isis.core.metamodel.facets.actions.action.invocation.IdentifierUtil;
 import org.apache.isis.core.metamodel.object.ManagedObject;
-import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
+import org.apache.isis.core.metamodel.object.ProtoObject;
 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.ObjectActionParameter;
 import org.apache.isis.core.metamodel.spec.feature.ObjectFeature;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.schema.cmd.v2.ActionDto;
 import org.apache.isis.schema.cmd.v2.ParamDto;
 import org.apache.isis.schema.cmd.v2.PropertyDto;
@@ -60,7 +60,7 @@ import lombok.Value;
 import lombok.val;
 
 public abstract class SchemaValueMarshallerAbstract
-implements SchemaValueMarshaller {
+implements SchemaValueMarshaller, HasMetaModelContext {
 
     @Value(staticConstructor = "of")
     public static class Context<T> {
@@ -214,9 +214,8 @@ implements SchemaValueMarshaller {
     public final ManagedObject recoverReferenceFrom(
             final @NonNull OidDto oidDto) {
         val bookmark = Bookmark.forOidDto(oidDto);
-        val spec = getSpecificationLoader().specForLogicalTypeNameElseFail(bookmark.getLogicalTypeName());
-        val loadRequest = ObjectLoader.Request.of(spec, bookmark);
-        return spec.getMetaModelContext().getObjectManager().loadObject(loadRequest);
+        return getObjectManager()
+                .loadObject(ProtoObject.resolveElseFail(getSpecificationLoader(), bookmark));
     }
 
     @Override
@@ -360,7 +359,6 @@ implements SchemaValueMarshaller {
 
     // -- DEPENDENCIES
 
-    protected abstract SpecificationLoader getSpecificationLoader();
     protected abstract ValueSemanticsResolver getValueSemanticsResolver();
 
 }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java
index 3f8a406b1f..9e3873e1d4 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java
@@ -32,16 +32,17 @@ import org.apache.isis.applib.value.semantics.ValueDecomposition;
 import org.apache.isis.applib.value.semantics.ValueSemanticsResolver;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 import org.apache.isis.core.metamodel.object.PackedManagedObject;
 import org.apache.isis.core.metamodel.services.schema.SchemaValueMarshallerAbstract;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
 import org.apache.isis.schema.common.v2.CollectionDto;
 import org.apache.isis.schema.common.v2.TypedTupleDto;
 import org.apache.isis.schema.common.v2.ValueType;
 import org.apache.isis.schema.common.v2.ValueWithTypeDto;
 
+import lombok.Getter;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.val;
@@ -54,8 +55,8 @@ import lombok.val;
 public class SchemaValueMarshallerDefault
 extends SchemaValueMarshallerAbstract {
 
-    @Inject private ValueSemanticsResolver valueSemanticsResolver;
-    @Inject private SpecificationLoader specLoader;
+    @Inject @Getter(onMethod_ = {@Override}) private ValueSemanticsResolver valueSemanticsResolver;
+    @Inject @Getter(onMethod_ = {@Override}) private MetaModelContext metaModelContext;
 
     // -- RECORD VALUES INTO DTO
 
@@ -158,16 +159,4 @@ extends SchemaValueMarshallerAbstract {
                 .compose(ValueDecomposition.ofComposite(typedTupleDto));
     }
 
-    // -- DEPENDENCIES
-
-    @Override
-    protected final SpecificationLoader getSpecificationLoader() {
-        return specLoader;
-    }
-
-    @Override
-    protected ValueSemanticsResolver getValueSemanticsResolver() {
-        return valueSemanticsResolver;
-    }
-
 }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DelegatingInvocationHandlerDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DelegatingInvocationHandlerDefault.java
index cd8752bdf6..bb5cc9c200 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DelegatingInvocationHandlerDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DelegatingInvocationHandlerDefault.java
@@ -25,11 +25,11 @@ import org.apache.isis.applib.services.wrapper.WrapperFactory;
 import org.apache.isis.applib.services.wrapper.control.SyncControl;
 import org.apache.isis.applib.services.wrapper.events.InteractionEvent;
 import org.apache.isis.commons.internal._Constants;
+import org.apache.isis.commons.internal.base._Blackhole;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 import org.apache.isis.core.metamodel.object.ManagedObjects;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
-import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
 
 import lombok.Getter;
 import lombok.NonNull;
@@ -85,9 +85,7 @@ public class DelegatingInvocationHandlerDefault<T> implements DelegatingInvocati
             return;
         }
 
-        objectManager.bookmarkObject(adapter)
-            .map(bookmark->ObjectLoader.Request.of(adapter.getSpecification(), bookmark))
-            .ifPresent(objectManager::loadObject);
+        _Blackhole.consume(adapter.getPojo());
     }
 
     protected void resolveIfRequired(final Object domainObject) {
diff --git a/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/FullCalendarWithEventHandling.java b/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/FullCalendarWithEventHandling.java
index 2e01d7b032..a453066814 100644
--- a/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/FullCalendarWithEventHandling.java
+++ b/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/FullCalendarWithEventHandling.java
@@ -22,8 +22,8 @@ import org.apache.wicket.RestartResponseException;
 
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.metamodel.object.ProtoObject;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
-import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.runtime.context.IsisAppCommonContext;
 import org.apache.isis.extensions.fullcalendar.wkt.fullcalendar.CalendarConfig;
@@ -34,9 +34,10 @@ import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.model.util.WktContext;
 import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage;
 
-import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
 import lombok.val;
 
+import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
+
 final class FullCalendarWithEventHandling extends FullCalendar {
 
     private static final long serialVersionUID = 1L;
@@ -72,8 +73,8 @@ final class FullCalendarWithEventHandling extends FullCalendar {
         final ObjectManager objectManager = commonContext.getObjectManager();
         final IsisAppCommonContext webAppCommonContext = IsisAppCommonContext.of(metaModelContext);
 
-        val spec = specificationLoader.specForLogicalTypeName(bookmark.getLogicalTypeName()).orElse(null);
-        val managedObject = objectManager.loadObject(ObjectLoader.Request.of(spec, bookmark));
+        val managedObject = objectManager
+                .loadObject(ProtoObject.resolveElseFail(specificationLoader, bookmark));
 
         final EntityModel entityModel = EntityModel.ofAdapter(webAppCommonContext, managedObject);