You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/06/03 10:24:50 UTC

[isis] 01/02: ISIS-2371: refactor MangedObject utilities in preparation of a fix

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 0132cb0867e5a9b8c2793b332114ae5693c45637
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Jun 3 12:24:18 2020 +0200

    ISIS-2371: refactor MangedObject utilities in preparation of a fix
---
 ...ctionInvocationFacetForDomainEventAbstract.java |   5 +-
 .../actions/action/invocation/CommandUtil.java     |   3 +-
 .../BooleanValueSemanticsProviderAbstract.java     |   3 +-
 .../metamodel/interactions/InteractionHead.java    |   5 +-
 .../interactions/managed/ManagedProperty.java      |   1 -
 .../objectmanager/refresh/ObjectRefresher.java     |   1 -
 .../isis/core/metamodel/spec/ManagedObject.java    | 198 ++-------------------
 .../metamodel/spec/ManagedObjectInternalUtil.java  | 101 ++++++++++-
 .../isis/core/metamodel/spec/ManagedObjects.java   | 135 +++++++++++++-
 .../core/metamodel/util/snapshot/XmlSnapshot.java  |  10 +-
 .../transaction/AdapterAndProperty.java            |   3 +-
 .../bookmarks/BookmarkServiceDefault.java          |   4 +-
 .../command/CommandDtoServiceInternalDefault.java  |   3 +-
 .../publish/PublishedObjectsDefault.java           |   3 +-
 .../DelegatingInvocationHandlerDefault.java        |   3 +-
 .../ui/component/EventProviderAbstract.java        |   3 +-
 .../persistence/IsisPersistenceSessionJdoBase.java |   3 +-
 .../common/model/binding/UiComponentFactory.java   |   3 +-
 .../AbstractObjectMemberReprRenderer.java          |   3 +-
 .../domainobjects/DomainObjectLinkTo.java          |   3 +-
 .../domainobjects/DomainObjectReprRenderer.java    |  19 +-
 .../domainobjects/ObjectActionReprRenderer.java    |   4 +-
 .../ObjectCollectionReprRenderer.java              |   3 +-
 .../viewer/context/ResourceContext.java            |   3 +-
 .../viewer/wicket/model/models/ActionModel.java    |   5 +-
 .../wicket/model/models/BookmarkTreeNode.java      |   4 +-
 .../wicket/model/models/ManagedObjectModel.java    |   5 +-
 .../wicket/model/models/PageParameterUtil.java     |   9 +-
 .../wicket/model/models/ScalarPropertyModel.java   |   2 +-
 .../entityactions/EntityActionLinkFactory.java     |   4 +-
 .../columns/ObjectAdapterTitleColumn.java          |   3 +-
 .../components/scalars/ScalarPanelAbstract2.java   |   4 +-
 .../components/tree/IsisToWicketTreeAdapter.java   |   3 +-
 .../ui/components/unknown/UnknownModelPanel.java   |   4 +-
 .../isis/viewer/wicket/ui/pages/home/HomePage.java |   4 +-
 .../integration/ConverterForObjectAdapter.java     |   5 +-
 .../ConverterForObjectAdapterMemento.java          |   4 +-
 .../services/mementos/ObjectMementoLegacy.java     |   7 +-
 .../mementos/ObjectMementoServiceWicket.java       |   3 +-
 39 files changed, 333 insertions(+), 255 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
index c11f987..1caf339 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
@@ -65,6 +65,7 @@ import org.apache.isis.core.metamodel.interactions.InteractionHead;
 import org.apache.isis.core.metamodel.services.ixn.InteractionDtoServiceInternal;
 import org.apache.isis.core.metamodel.services.publishing.PublisherDispatchService;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.schema.ixn.v2.ActionInvocationDto;
@@ -313,7 +314,7 @@ implements ImperativeFacet {
             // don't trample over any existing result, eg subsequent mixins.
             return;
         }
-        if(ManagedObject.isNullOrUnspecifiedOrEmpty(resultAdapter)) {
+        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(resultAdapter)) {
             return;
         }
 
@@ -350,7 +351,7 @@ implements ImperativeFacet {
             final ManagedObject resultAdapter,
             final InteractionInitiatedBy interactionInitiatedBy) {
 
-        if(ManagedObject.isNullOrUnspecifiedOrEmpty(resultAdapter)) { 
+        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(resultAdapter)) { 
             return null;
         }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/CommandUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/CommandUtil.java
index 1815530..00509f0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/CommandUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/CommandUtil.java
@@ -27,6 +27,7 @@ import org.apache.isis.core.commons.internal.collections._Arrays;
 import org.apache.isis.core.metamodel.commons.StringExtensions;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 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;
@@ -84,7 +85,7 @@ public class CommandUtil {
     }
 
     public static Bookmark bookmarkFor(final ManagedObject adapter) {
-        return ManagedObject.bookmark(adapter)
+        return ManagedObjects.bookmark(adapter)
                 .orElse(null);
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/booleans/BooleanValueSemanticsProviderAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/booleans/BooleanValueSemanticsProviderAbstract.java
index 61b8ac6..6326796 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/booleans/BooleanValueSemanticsProviderAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/booleans/BooleanValueSemanticsProviderAbstract.java
@@ -25,6 +25,7 @@ import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.object.parseable.TextEntryParseException;
 import org.apache.isis.core.metamodel.facets.object.value.vsp.ValueSemanticsProviderAndFacetAbstract;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 
 
 public abstract class BooleanValueSemanticsProviderAbstract
@@ -111,7 +112,7 @@ extends ValueSemanticsProviderAndFacetAbstract<Boolean> implements BooleanValueF
 
     @Override
     public boolean isSet(ManagedObject adapter) {
-        if (ManagedObject.isNullOrUnspecifiedOrEmpty(adapter)) {
+        if (ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)) {
             return false;
         }
         final Object object = adapter.getPojo();
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/InteractionHead.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/InteractionHead.java
index 73c30b0..43ac726 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/InteractionHead.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/InteractionHead.java
@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
 
 import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 
 import lombok.AccessLevel;
 import lombok.Getter;
@@ -51,11 +52,11 @@ public class InteractionHead {
     
     /** factory with consistency checks */
     public static InteractionHead of(@NonNull ManagedObject owner, @NonNull ManagedObject target) {
-        if(ManagedObject.isSpecified(owner) 
+        if(ManagedObjects.isSpecified(owner) 
                 && owner.getSpecification().getBeanSort().isMixin()) {
             throw _Exceptions.unrecoverableFormatted("unexpected: owner is a mixin %s", owner);
         }
-        if(ManagedObject.isSpecified(target)                    
+        if(ManagedObjects.isSpecified(target)                    
                 && target.getSpecification().getBeanSort().isMixin()
                 && target.getPojo()==null) {
             throw _Exceptions.unrecoverableFormatted("target not spec. %s", target);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedProperty.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedProperty.java
index c94507c..046c4d0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedProperty.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedProperty.java
@@ -59,7 +59,6 @@ public final class ManagedProperty extends ManagedMember {
     private ManagedProperty(
             final @NonNull ManagedObject owner, 
             final @NonNull OneToOneAssociation property) {
-        
         super(owner);
         this.property = property;
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher.java
index dcbf1ec..4b83616 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher.java
@@ -20,7 +20,6 @@ package org.apache.isis.core.metamodel.objectmanager.refresh;
 
 import org.apache.isis.core.commons.handler.ChainOfResponsibility;
 import org.apache.isis.core.commons.internal.collections._Lists;
-import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 
 import lombok.val;
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 898aa99..aa7b61c 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
@@ -35,7 +35,6 @@ import javax.annotation.Nullable;
 
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.domain.DomainObjectList;
-import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.repository.EntityState;
 import org.apache.isis.core.commons.collections.Can;
 import org.apache.isis.core.commons.internal.base._Lazy;
@@ -53,6 +52,7 @@ import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 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.ObjectManager;
 import org.apache.isis.core.metamodel.objectmanager.create.ObjectCreator;
 import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
@@ -92,64 +92,11 @@ public interface ManagedObject {
      */
     Optional<RootOid> getRootOid();
     
-    // -- SHORTCUTS
-    
-    static Optional<RootOid> identify(@Nullable ManagedObject adapter) {
-        return isSpecified(adapter)  ? adapter.getRootOid() : Optional.empty(); 
-    }
-    
-    static RootOid identifyElseFail(@Nullable ManagedObject adapter) {
-        return identify(adapter)
-                .orElseThrow(()->_Exceptions.illegalArgument("cannot identify %s", adapter));
-    }
-    
-    static Optional<Bookmark> bookmark(@Nullable ManagedObject adapter) {
-        return identify(adapter)
-                .map(RootOid::asBookmark);
-    }
-    
-    static Bookmark bookmarkElseFail(@Nullable ManagedObject adapter) {
-        return bookmark(adapter)
-                .orElseThrow(()->_Exceptions.illegalArgument("cannot bookmark %s", adapter));
-    }
-    
-    static Optional<String> stringify(@Nullable ManagedObject adapter) {
-        return identify(adapter)
-                .map(RootOid::enString);
-    }
-    
-    static String stringifyElseFail(@Nullable ManagedObject adapter) {
-        return stringify(adapter)
-                .orElseThrow(()->_Exceptions.illegalArgument("cannot stringify %s", adapter));
-    }
-    
-    
     // -- EMPTY
     
-    static class UnspecifiedHolder {
-        private static final ManagedObject UNSPECIFIED = new ManagedObject() {
-
-            @Override
-            public ObjectSpecification getSpecification() {
-                throw _Exceptions.unsupportedOperation();
-            }
-
-            @Override
-            public Object getPojo() {
-                return null;
-            }
-
-            @Override
-            public Optional<RootOid> getRootOid() {
-                return Optional.empty();
-            }
-            
-        };
-    }
-    
     /** has no ObjectSpecification and no value */
     static ManagedObject unspecified() {
-        return UnspecifiedHolder.UNSPECIFIED;
+        return ManagedObjectInternalUtil.UNSPECIFIED;
     }
     
     /** has an ObjectSpecification, but no value */
@@ -158,8 +105,6 @@ public interface ManagedObject {
     }
     
 
-    
-
     // -- SIMPLE
 
     @Value 
@@ -185,10 +130,8 @@ public interface ManagedObject {
         }
         
         // -- LAZY ID HANDLING
-        private final _Lazy<Optional<RootOid>> rootOidLazy = _Lazy.threadSafe(this::identify);  
-        private Optional<RootOid> identify() {
-            return Optional.ofNullable(ManagedObjectInternalUtil._identify(this));
-        }
+        private final _Lazy<Optional<RootOid>> rootOidLazy = 
+                _Lazy.threadSafe(()->ManagedObjectInternalUtil.identify(this));  
 
         
     }
@@ -203,7 +146,7 @@ public interface ManagedObject {
         @Getter @NonNull private final Object pojo;
         
         @Getter(lazy=true, onMethod = @__(@Override)) 
-        private final Optional<RootOid> rootOid = Optional.ofNullable(ManagedObjectInternalUtil._identify(this));
+        private final Optional<RootOid> rootOid = ManagedObjectInternalUtil.identify(this);
 
         private final _Lazy<ObjectSpecification> specification = _Lazy.threadSafe(this::loadSpec);
 
@@ -242,72 +185,16 @@ public interface ManagedObject {
     }
 
     default String titleString(@Nullable ManagedObject contextAdapterIfAny) {
-        return TitleUtil.titleString(this, contextAdapterIfAny);
+        return ManagedObjectInternalUtil.titleString(this, contextAdapterIfAny);
     }
     
-    @Deprecated // move to ManagedObjects
-    @NoArgsConstructor(access = AccessLevel.PRIVATE)
-    static final class TitleUtil {
-
-        private static String titleString(@Nullable ManagedObject managedObject, @Nullable ManagedObject contextAdapterIfAny) {
-            
-            if(!ManagedObject.isSpecified(managedObject)) {
-                return "unspecified object";
-            }
-            
-            if (managedObject.getSpecification().isParentedOrFreeCollection()) {
-                final CollectionFacet facet = managedObject.getSpecification().getFacet(CollectionFacet.class);
-                return collectionTitleString(managedObject, facet);
-            } else {
-                return objectTitleString(managedObject, contextAdapterIfAny);
-            }
-        }
-
-        private static String objectTitleString(ManagedObject managedObject, ManagedObject contextAdapterIfAny) {
-            if (managedObject.getPojo() instanceof String) {
-                return (String) managedObject.getPojo();
-            }
-            final ObjectSpecification specification = managedObject.getSpecification();
-            String title = specification.getTitle(contextAdapterIfAny, managedObject);
-
-            if (title == null) {
-                title = getDefaultTitle(managedObject);
-            }
-            return title;
-        }
-
-        private static String collectionTitleString(ManagedObject managedObject, final CollectionFacet facet) {
-            final int size = facet.size(managedObject);
-            final ObjectSpecification elementSpecification = managedObject.getElementSpecification().orElse(null);
-            if (elementSpecification == null || elementSpecification.getFullIdentifier().equals(Object.class.getName())) {
-                switch (size) {
-                case -1:
-                    return "Objects";
-                case 0:
-                    return "No objects";
-                case 1:
-                    return "1 object";
-                default:
-                    return size + " objects";
-                }
-            } else {
-                switch (size) {
-                case -1:
-                    return elementSpecification.getPluralName();
-                case 0:
-                    return "No " + elementSpecification.getPluralName();
-                case 1:
-                    return "1 " + elementSpecification.getSingularName();
-                default:
-                    return size + " " + elementSpecification.getPluralName();
-                }
-            }
-        }
-
-        private static String getDefaultTitle(ManagedObject managedObject) {
-            return "A" + (" " + managedObject.getSpecification().getSingularName()).toLowerCase();
-        }
-
+    // -- SHORTCUT - OBJECT MANAGER
+    
+    default ObjectManager getObjectManager() {
+        return ManagedObjectInternalUtil.objectManager(this)
+                .orElseThrow(()->_Exceptions
+                        .illegalArgument("Can only retrieve ObjectManager from ManagedObjects "
+                                + "that are 'specified'."));
     }
 
     // -- SHORTCUT - ELEMENT SPECIFICATION
@@ -380,7 +267,7 @@ public interface ManagedObject {
 
     @Nullable
     public static Object unwrapSingle(@Nullable final ManagedObject adapter) {
-        return isSpecified(adapter)
+        return ManagedObjects.isSpecified(adapter)
                 ? adapter.getPojo() 
                 : null;
     }
@@ -458,56 +345,7 @@ public interface ManagedObject {
                 .collect(_Sets.toUnmodifiable());
     }
 
-    // -- SHORTCUTS
-
-    public static String getDomainType(ManagedObject adapter) {
-        if(!isSpecified(adapter)) {
-            return null;
-        }
-        return adapter.getSpecification().getSpecId().asString();
-    }
-
-    // -- BASIC PREDICATES
-
-    static boolean isEntity(ManagedObject adapter) {
-        if(!isSpecified(adapter)) {
-            return false;
-        }
-        return adapter.getSpecification().isEntity();
-    }
 
-    static boolean isValue(ManagedObject adapter) {
-        if(!isSpecified(adapter)) {
-            return false;
-        }
-        return adapter.getSpecification().isValue();
-    }
-
-    /**
-     * @return whether the corresponding type can be mapped onto a REFERENCE (schema) or an Oid,
-     * that is the type is 'identifiable' (aka 'referencable' or 'bookmarkable') 
-     */
-    static boolean isIdentifiable(@Nullable ManagedObject adapter) {
-        if(!isSpecified(adapter)) {
-            return false;
-        }
-        val spec = adapter.getSpecification();
-        return spec.isIdentifiable();
-    }
-
-    static boolean isNullOrUnspecifiedOrEmpty(@Nullable ManagedObject adapter) {
-        if(adapter==null || adapter==ManagedObject.unspecified()) {
-            return true;
-        }
-        return adapter.getPojo()==null;
-    }
-    
-    /** whether has at least a spec */
-    static boolean isSpecified(@Nullable ManagedObject adapter) {
-        return adapter!=null && adapter!=ManagedObject.unspecified();
-    }
-
-    
     // -- VISIBILITY UTILITIES
 
     @Deprecated // move to ManagedObjects
@@ -570,7 +408,7 @@ public interface ManagedObject {
                 ManagedObject adapter,
                 InteractionInitiatedBy interactionInitiatedBy) {
 
-            if(isNullOrUnspecifiedOrEmpty(adapter)) {
+            if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)) {
                 // a choices list could include a null (eg example in ToDoItems#choices1Categorized()); want to show as "visible"
                 return true;
             }
@@ -745,7 +583,7 @@ public interface ManagedObject {
     // -- DEPRECATIONS (REFACTORING)
 
     static String _instanceId(ManagedObject adapter) {
-        val identifier = identifyElseFail(adapter).getIdentifier();
+        val identifier = ManagedObjects.identifyElseFail(adapter).getIdentifier();
         return identifier; 
     }
     
@@ -779,7 +617,7 @@ public interface ManagedObject {
             ManagedObject first,
             ManagedObject second) {
 
-        if(ManagedObject.isIdentifiable(first) && ManagedObject.isSpecified(second)) {
+        if(ManagedObjects.isIdentifiable(first) && ManagedObjects.isSpecified(second)) {
 
             val refSpec = second.getSpecification();
 
@@ -878,6 +716,8 @@ public interface ManagedObject {
         return adapter.getSpecification().getBeanSort().isCollection();
     }
 
+    
+
 
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjectInternalUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjectInternalUtil.java
index a5cb1be..74dc02f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjectInternalUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjectInternalUtil.java
@@ -19,8 +19,15 @@
 
 package org.apache.isis.core.metamodel.spec;
 
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.metamodel.facets.collections.CollectionFacet;
+import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
 
 import lombok.experimental.UtilityClass;
 
@@ -28,16 +35,98 @@ import lombok.experimental.UtilityClass;
  * @since 2.0
  */
 @UtilityClass
-class ManagedObjectInternalUtil {
+final class ManagedObjectInternalUtil {
+
+
+    static final ManagedObject UNSPECIFIED = new ManagedObject() {
+
+        @Override
+        public ObjectSpecification getSpecification() {
+            throw _Exceptions.unsupportedOperation();
+        }
+
+        @Override
+        public Object getPojo() {
+            return null;
+        }
+
+        @Override
+        public Optional<RootOid> getRootOid() {
+            return Optional.empty();
+        }
+        
+    };
 
-    // -- INTERNAL
+    static Optional<ObjectManager> objectManager(@Nullable ManagedObject adapter) {
+        return ManagedObjects.spec(adapter)
+        .map(ObjectSpecification::getMetaModelContext)
+        .map(MetaModelContext::getObjectManager);
+    }
+    
+    static Optional<RootOid> identify(@Nullable ManagedObject adapter) {
+        return objectManager(adapter)
+                .map(objectManager->objectManager.identifyObject(adapter)); 
+    }
+    
+    // -- TITLE SUPPORT
+    
+    static String titleString(@Nullable ManagedObject managedObject, @Nullable ManagedObject contextAdapterIfAny) {
+        
+        if(!ManagedObjects.isSpecified(managedObject)) {
+            return "unspecified object";
+        }
+        
+        if (managedObject.getSpecification().isParentedOrFreeCollection()) {
+            final CollectionFacet facet = managedObject.getSpecification().getFacet(CollectionFacet.class);
+            return collectionTitleString(managedObject, facet);
+        } else {
+            return objectTitleString(managedObject, contextAdapterIfAny);
+        }
+    }
+
+    private static String objectTitleString(ManagedObject managedObject, ManagedObject contextAdapterIfAny) {
+        if (managedObject.getPojo() instanceof String) {
+            return (String) managedObject.getPojo();
+        }
+        final ObjectSpecification specification = managedObject.getSpecification();
+        String title = specification.getTitle(contextAdapterIfAny, managedObject);
+
+        if (title == null) {
+            title = getDefaultTitle(managedObject);
+        }
+        return title;
+    }
 
-    static MetaModelContext _mmc(ManagedObject adapter) {
-        return adapter.getSpecification().getMetaModelContext();
+    private static String collectionTitleString(ManagedObject managedObject, final CollectionFacet facet) {
+        final int size = facet.size(managedObject);
+        final ObjectSpecification elementSpecification = managedObject.getElementSpecification().orElse(null);
+        if (elementSpecification == null || elementSpecification.getFullIdentifier().equals(Object.class.getName())) {
+            switch (size) {
+            case -1:
+                return "Objects";
+            case 0:
+                return "No objects";
+            case 1:
+                return "1 object";
+            default:
+                return size + " objects";
+            }
+        } else {
+            switch (size) {
+            case -1:
+                return elementSpecification.getPluralName();
+            case 0:
+                return "No " + elementSpecification.getPluralName();
+            case 1:
+                return "1 " + elementSpecification.getSingularName();
+            default:
+                return size + " " + elementSpecification.getPluralName();
+            }
+        }
     }
 
-    static RootOid _identify(ManagedObject adapter) {
-        return _mmc(adapter).getObjectManager().identifyObject(adapter); 
+    private static String getDefaultTitle(ManagedObject managedObject) {
+        return "A" + (" " + managedObject.getSpecification().getSingularName()).toLowerCase();
     }
 
 }
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 cc9aee7..eb82fee 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
@@ -20,14 +20,22 @@ package org.apache.isis.core.metamodel.spec;
 
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.Optional;
 
 import javax.annotation.Nullable;
 
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.repository.EntityState;
+import org.apache.isis.core.commons.internal.assertions._Assert;
 import org.apache.isis.core.commons.internal.base._NullSafe;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
+import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
+import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 
+import lombok.NonNull;
 import lombok.val;
 import lombok.experimental.UtilityClass;
 
@@ -38,6 +46,84 @@ import lombok.experimental.UtilityClass;
  */
 @UtilityClass
 public final class ManagedObjects {
+    
+    // -- CATEGORISATION
+
+    public static boolean isNullOrUnspecifiedOrEmpty(@Nullable ManagedObject adapter) {
+        if(adapter==null || adapter==ManagedObject.unspecified()) {
+            return true;
+        }
+        return adapter.getPojo()==null;
+    }
+    
+    /** whether has at least a spec */
+    public static boolean isSpecified(@Nullable ManagedObject adapter) {
+        return adapter!=null && adapter!=ManagedObject.unspecified();
+    }
+    
+    /**
+     * @return whether the corresponding type can be mapped onto a REFERENCE (schema) or an Oid,
+     * that is the type is 'identifiable' (aka 'referencable' or 'bookmarkable') 
+     */
+    public static boolean isIdentifiable(@Nullable ManagedObject adapter) {
+        return spec(adapter)
+                .map(ObjectSpecification::isIdentifiable)
+                .orElse(false);
+    }
+    
+    public static boolean isEntity(ManagedObject adapter) {
+        return spec(adapter)
+                .map(ObjectSpecification::isEntity)
+                .orElse(false);
+    }
+
+    public static boolean isValue(ManagedObject adapter) {
+        return spec(adapter)
+                .map(ObjectSpecification::isValue)
+                .orElse(false);
+    }
+    
+    public static Optional<String> getDomainType(ManagedObject adapter) {
+        return spec(adapter)
+                .map(ObjectSpecification::getSpecId)
+                .map(ObjectSpecId::asString);
+    }
+    
+    // -- IDENTIFICATION
+    
+    public static Optional<ObjectSpecification> spec(@Nullable ManagedObject adapter) {
+        return isSpecified(adapter) ? Optional.of(adapter.getSpecification()) : Optional.empty(); 
+    }
+    
+    public static Optional<RootOid> identify(@Nullable ManagedObject adapter) {
+        return isSpecified(adapter) ? adapter.getRootOid() : Optional.empty(); 
+    }
+    
+    public static RootOid identifyElseFail(@Nullable ManagedObject adapter) {
+        return identify(adapter)
+                .orElseThrow(()->_Exceptions.illegalArgument("cannot identify %s", adapter));
+    }
+    
+    public static Optional<Bookmark> bookmark(@Nullable ManagedObject adapter) {
+        return identify(adapter)
+                .map(RootOid::asBookmark);
+    }
+    
+    public static Bookmark bookmarkElseFail(@Nullable ManagedObject adapter) {
+        return bookmark(adapter)
+                .orElseThrow(()->_Exceptions.illegalArgument("cannot bookmark %s", adapter));
+    }
+    
+    public static Optional<String> stringify(@Nullable ManagedObject adapter) {
+        return identify(adapter)
+                .map(RootOid::enString);
+    }
+    
+    public static String stringifyElseFail(@Nullable ManagedObject adapter) {
+        return stringify(adapter)
+                .orElseThrow(()->_Exceptions.illegalArgument("cannot stringify %s", adapter));
+    }
+
 
     // -- COMPARE UTILITIES
 
@@ -49,7 +135,7 @@ public final class ManagedObjects {
 
         final Comparator<ManagedObject> comparator = ascending 
                 ? NATURAL_NULL_FIRST 
-                        : NATURAL_NULL_FIRST.reversed();
+                : NATURAL_NULL_FIRST.reversed();
 
         return (p, q) -> {
             val pSort = sortProperty.get(p, InteractionInitiatedBy.FRAMEWORK);
@@ -121,5 +207,52 @@ public final class ManagedObjects {
         return str.length() < maxLength ? str : str.substring(0, maxLength - 3) + suffix;
     }
 
+    // -- ENTITY UTILITIES
+    
+    /**
+     * @param managedObject
+     * @return managedObject
+     * @throws AssertionError if managedObject is a detached entity  
+     */
+    public static ManagedObject requiresAttached(@NonNull ManagedObject managedObject) {
+        val entityState = ManagedObject._entityState(managedObject);
+        if(entityState.isPersistable()) {
+            // ensure we have an attached entity
+            _Assert.assertEquals(
+                    EntityState.PERSISTABLE_ATTACHED, 
+                    entityState,
+                    ()-> String.format("entity %s is required to be attached (not detached) at this stage", 
+                            managedObject.getSpecification().getSpecId()));
+        }
+        return managedObject;
+    }
+    
+    @Nullable
+    public static ManagedObject reattach(@Nullable ManagedObject managedObject) {
+        if(isNullOrUnspecifiedOrEmpty(managedObject)) {
+            return managedObject;
+        }
+        val entityState = ManagedObject._entityState(managedObject);
+        if(!entityState.isPersistable()) {
+            return managedObject;
+        }
+        if(!entityState.isDetached()) {
+            return managedObject;
+        }
+        
+        val objectIdentifier = identify(managedObject)
+                .map(RootOid::getIdentifier);
+                
+        if(!objectIdentifier.isPresent()) {
+            return managedObject;
+        }
+        
+        val objectLoadRequest = ObjectLoader.Request.of(
+                managedObject.getSpecification(), 
+                objectIdentifier.get());
+        
+        return managedObject.getObjectManager().loadObject(objectLoadRequest);
+    }
+
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/snapshot/XmlSnapshot.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/snapshot/XmlSnapshot.java
index 7651f49..41ad5b2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/snapshot/XmlSnapshot.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/snapshot/XmlSnapshot.java
@@ -19,7 +19,6 @@
 
 package org.apache.isis.core.metamodel.util.snapshot;
 
-import java.io.StringWriter;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
@@ -32,12 +31,6 @@ import java.util.stream.Collectors;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -57,6 +50,7 @@ import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
 import org.apache.isis.core.metamodel.facets.object.parseable.ParseableFacet;
 import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 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;
@@ -857,7 +851,7 @@ public class XmlSnapshot implements Snapshot {
             }
             return fakeOid;
         } else {
-            return ManagedObject.stringifyElseFail(adapter);
+            return ManagedObjects.stringifyElseFail(adapter);
         }
     }
 
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/transaction/AdapterAndProperty.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/transaction/AdapterAndProperty.java
index 48efe12..d015fc0 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/transaction/AdapterAndProperty.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/transaction/AdapterAndProperty.java
@@ -21,6 +21,7 @@ package org.apache.isis.core.runtime.persistence.transaction;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 
 import lombok.EqualsAndHashCode;
@@ -51,7 +52,7 @@ public class AdapterAndProperty {
         this.property = property;
         this.propertyId = property.getId();
 
-        this.bookmark = ManagedObject.bookmarkElseFail(adapter);
+        this.bookmark = ManagedObjects.bookmarkElseFail(adapter);
         this.bookmarkStr = bookmark.toString();
         
     }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/bookmarks/BookmarkServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/bookmarks/BookmarkServiceDefault.java
index 17925aa..4bc848a 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/bookmarks/BookmarkServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/bookmarks/BookmarkServiceDefault.java
@@ -43,7 +43,7 @@ import org.apache.isis.core.commons.internal.memento._Mementos.SerializingAdapte
 import org.apache.isis.core.metamodel.adapter.oid.ObjectNotFoundException;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
 import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 
@@ -138,7 +138,7 @@ public class BookmarkServiceDefault implements BookmarkService, SerializingAdapt
             return null;
         }
         val adapter = objectManager.adapt(unwrapped(domainObject)); 
-        if(!ManagedObject.isIdentifiable(adapter)){
+        if(!ManagedObjects.isIdentifiable(adapter)){
             // eg values cannot be bookmarked
             return null;
         }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoServiceInternalDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoServiceInternalDefault.java
index 82def75..27384e2 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoServiceInternalDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoServiceInternalDefault.java
@@ -39,6 +39,7 @@ import org.apache.isis.applib.util.schema.CommonDtoUtils;
 import org.apache.isis.core.metamodel.facets.actions.action.invocation.CommandUtil;
 import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 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;
@@ -107,7 +108,7 @@ public class CommandDtoServiceInternalDefault implements CommandDtoServiceIntern
         dto.setTransactionId(transactionId);
 
         for (val targetAdapter : targetAdapters) {
-            final Bookmark bookmark = ManagedObject.bookmarkElseFail(targetAdapter);
+            final Bookmark bookmark = ManagedObjects.bookmarkElseFail(targetAdapter);
             final OidsDto targets = CommandDtoUtils.targetsFor(dto);
             targets.getOid().add(bookmark.toOidDto());
         }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/PublishedObjectsDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/PublishedObjectsDefault.java
index 76cb8e0..cb1fd3c 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/PublishedObjectsDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/PublishedObjectsDefault.java
@@ -35,6 +35,7 @@ import org.apache.isis.core.commons.internal.collections._Maps;
 import org.apache.isis.core.commons.internal.collections._Multimaps.ListMultimap;
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.schema.chg.v2.ChangesDto;
 import org.apache.isis.schema.chg.v2.ObjectsDto;
 import org.apache.isis.schema.common.v2.OidsDto;
@@ -189,7 +190,7 @@ public class PublishedObjectsDefault implements PublishedObjects, RepresentsInte
 
         _NullSafe.stream(adaptersByChange.get().get(kind))
         .map((final ManagedObject adapter) -> 
-            ManagedObject.identify(adapter)
+            ManagedObjects.identify(adapter)
             .map(RootOid::asOidDto)
             .orElse(null)
         )
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 7ad4e7d..d75a83b 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
@@ -30,6 +30,7 @@ import org.apache.isis.core.commons.internal._Constants;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
 import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -86,7 +87,7 @@ public class DelegatingInvocationHandlerDefault<T> implements DelegatingInvocati
         if(adapter==null) {
             return;
         }
-        if(!ManagedObject.isEntity(adapter)) {
+        if(!ManagedObjects.isEntity(adapter)) {
             return;
         }
         
diff --git a/extensions/vw/fullcalendar/ui/src/main/java/org/apache/isis/extensions/fullcalendar/ui/component/EventProviderAbstract.java b/extensions/vw/fullcalendar/ui/src/main/java/org/apache/isis/extensions/fullcalendar/ui/component/EventProviderAbstract.java
index 135814b..a1bfcf2 100644
--- a/extensions/vw/fullcalendar/ui/src/main/java/org/apache/isis/extensions/fullcalendar/ui/component/EventProviderAbstract.java
+++ b/extensions/vw/fullcalendar/ui/src/main/java/org/apache/isis/extensions/fullcalendar/ui/component/EventProviderAbstract.java
@@ -33,6 +33,7 @@ import org.joda.time.Interval;
 
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.webapp.context.IsisWebAppCommonContext;
@@ -108,7 +109,7 @@ public abstract class EventProviderAbstract implements EventProvider {
             final ObjectSpecification spec = specificationLoader.loadSpecification(dereferencedObject.getClass());
             final ManagedObject dereferencedManagedObject = ManagedObject.of(spec, dereferencedObject);
 
-            final RootOid rootOid = ManagedObject.identify(dereferencedManagedObject).orElse(null);
+            final RootOid rootOid = ManagedObjects.identify(dereferencedManagedObject).orElse(null);
             if(rootOid!=null) {
 
                 final String oidStr = rootOid.enString();
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/persistence/IsisPersistenceSessionJdoBase.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/persistence/IsisPersistenceSessionJdoBase.java
index bee01ff..d7486e9 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/persistence/IsisPersistenceSessionJdoBase.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/persistence/IsisPersistenceSessionJdoBase.java
@@ -40,6 +40,7 @@ import org.apache.isis.core.metamodel.adapter.oid.Oid;
 import org.apache.isis.core.metamodel.commons.ToString;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.runtime.persistence.transaction.ChangedObjectsService;
 import org.apache.isis.persistence.jdo.applib.fixturestate.FixturesInstalledStateHolder;
@@ -221,7 +222,7 @@ abstract class IsisPersistenceSessionJdoBase implements IsisPersistenceSessionJd
             return null;
         }
         val adapter = ManagedObject.of(getSpecificationLoader().loadSpecification(pojo.getClass()), pojo);
-        return ManagedObject.identify(adapter).orElse(null);
+        return ManagedObjects.identify(adapter).orElse(null);
     }
 
     // -- HELPERS - SERVICE LOOKUP
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 52cb9ce..b2c84b6 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
@@ -31,6 +31,7 @@ import org.apache.isis.core.metamodel.interactions.managed.InteractionVeto;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedMember;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 import lombok.NonNull;
@@ -116,7 +117,7 @@ public interface UiComponentFactory<T> {
             //TODO do a type check before the cast, so we can throw a more detailed exception
             // that is, given type must be assignable from the actual pojo type 
             return Optional.ofNullable(managedProperty.getPropertyValue(where))
-                    .filter(_Predicates.not(ManagedObject::isNullOrUnspecifiedOrEmpty))
+                    .filter(_Predicates.not(ManagedObjects::isNullOrUnspecifiedOrEmpty))
                     .map(ManagedObject::getPojo)
                     .map(type::cast);
         }
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java
index 97dd891..1dc3546 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java
@@ -26,6 +26,7 @@ import org.apache.isis.core.metamodel.consent.Consent;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedMember;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectFeature;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
@@ -258,7 +259,7 @@ extends ReprRendererAbstract<R, ManagedMember> {
     }
 
     private void addDetailsLinkIfPersistent() {
-        if (!ManagedObject.isIdentifiable(objectAdapter)) {
+        if (!ManagedObjects.isIdentifiable(objectAdapter)) {
             return;
         }
         final JsonRepresentation link = linkTo.memberBuilder(Rel.DETAILS, objectMemberType, objectMember).build();
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectLinkTo.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectLinkTo.java
index 0956359..8afd363 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectLinkTo.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectLinkTo.java
@@ -19,6 +19,7 @@
 package org.apache.isis.viewer.restfulobjects.rendering.domainobjects;
 
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.viewer.restfulobjects.applib.Rel;
 import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
@@ -58,7 +59,7 @@ public class DomainObjectLinkTo implements ObjectAdapterLinkTo {
      * hook method
      */
     protected StringBuilder linkRef(StringBuilder buf) {
-        String domainType = ManagedObject.getDomainType(objectAdapter);
+        String domainType = ManagedObjects.getDomainType(objectAdapter).orElse("?");
         String instanceId = ManagedObject._instanceId(objectAdapter);
         return buf.append("objects/").append(domainType).append("/").append(instanceId);
     }
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectReprRenderer.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectReprRenderer.java
index bf46712..e7cbfd4 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectReprRenderer.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectReprRenderer.java
@@ -31,6 +31,7 @@ import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty;
 import org.apache.isis.core.metamodel.services.ServiceUtil;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 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;
@@ -58,7 +59,7 @@ public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectR
             final Rel rel, 
             final ManagedObject objectAdapter) {
         
-        String domainType = ManagedObject.getDomainType(objectAdapter);
+        String domainType = ManagedObjects.getDomainType(objectAdapter).orElse("?");
         String instanceId = ManagedObject._instanceId(objectAdapter);
         final String url = "objects/" + domainType + "/" + instanceId;
         return LinkBuilder.newBuilder(resourceContext, rel.getName(), RepresentationType.DOMAIN_OBJECT, url).withTitle(objectAdapter.titleString(null));
@@ -69,7 +70,7 @@ public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectR
             final ManagedObject objectAdapter) {
         
         final Rel rel = Rel.OBJECT_LAYOUT;
-        String domainType = ManagedObject.getDomainType(objectAdapter);
+        String domainType = ManagedObjects.getDomainType(objectAdapter).orElse("?");
         String instanceId = ManagedObject._instanceId(objectAdapter);
         final String url = "objects/" + domainType + "/" + instanceId + "/object-layout";
         return LinkBuilder.newBuilder(resourceContext, rel.getName(), RepresentationType.OBJECT_LAYOUT, url);
@@ -80,7 +81,7 @@ public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectR
             final ManagedObject objectAdapter) {
         
         final Rel rel = Rel.OBJECT_ICON;
-        String domainType = ManagedObject.getDomainType(objectAdapter);
+        String domainType = ManagedObjects.getDomainType(objectAdapter).orElse("?");
         String instanceId = ManagedObject._instanceId(objectAdapter);
         final String url = "objects/" + domainType + "/" + instanceId + "/image";
         return LinkBuilder.newBuilder(resourceContext, rel.getName(), RepresentationType.OBJECT_IMAGE, url);
@@ -172,7 +173,7 @@ public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectR
         if (!(mode.isArgs())) {
 
             // self, extensions.oid
-            if (ManagedObject.isIdentifiable(objectAdapter)) {
+            if (ManagedObjects.isIdentifiable(objectAdapter)) {
                 if (includesSelf) {
                     addLinkToSelf();
                 }
@@ -219,7 +220,7 @@ public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectR
 
             // extensions
             getExtensions().mapPut("isService", isService);
-            getExtensions().mapPut("isPersistent", ManagedObject.isIdentifiable(objectAdapter));
+            getExtensions().mapPut("isPersistent", ManagedObjects.isIdentifiable(objectAdapter));
             if(isService) {
                 final ObjectSpecification objectSpec = objectAdapter.getSpecification();
                 final DomainServiceLayoutFacet layoutFacet =
@@ -282,7 +283,7 @@ public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectR
     }
 
     private String getDomainType() {
-        return ManagedObject.getDomainType(objectAdapter);
+        return ManagedObjects.getDomainType(objectAdapter).orElse("?");
     }
 
     private String getInstanceId() {
@@ -290,7 +291,7 @@ public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectR
     }
 
     private String getOidStr() {
-        return ManagedObject.stringifyElseFail(objectAdapter);
+        return ManagedObjects.stringifyElseFail(objectAdapter);
     }
 
     private DomainObjectReprRenderer withMembers(final ManagedObject objectAdapter) {
@@ -409,7 +410,7 @@ public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectR
     }
 
     private void addPersistLinkIfTransientAndPersistable() {
-        if (ManagedObject.isIdentifiable(objectAdapter)) {
+        if (ManagedObjects.isIdentifiable(objectAdapter)) {
             return;
         }
         final DomainObjectReprRenderer renderer =
@@ -442,7 +443,7 @@ public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectR
         if(mode.isEventSerialization()) {
             return;
         }
-        if (!ManagedObject.isIdentifiable(objectAdapter)) {
+        if (!ManagedObjects.isIdentifiable(objectAdapter)) {
             return;
         }
         final boolean isService = objectAdapter.getSpecification().isManagedBean();
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
index 6aceb0e..cfb1687 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
@@ -29,7 +29,7 @@ import org.apache.isis.core.commons.internal.collections._Lists;
 import org.apache.isis.core.commons.internal.collections._Maps;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedAction;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
@@ -181,7 +181,7 @@ public class ObjectActionReprRenderer extends AbstractObjectMemberReprRenderer<O
                 .emptyModel();
         
         val defaultAdapter = param.getDefault(emptyPpm);
-        if (ManagedObject.isNullOrUnspecifiedOrEmpty(defaultAdapter)) {
+        if (ManagedObjects.isNullOrUnspecifiedOrEmpty(defaultAdapter)) {
             return null;
         }
         // REVIEW: previously was using the spec of the parameter, but think instead it should be the spec of the adapter itself
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectCollectionReprRenderer.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectCollectionReprRenderer.java
index 4899258..e0c5de0 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectCollectionReprRenderer.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectCollectionReprRenderer.java
@@ -29,6 +29,7 @@ import org.apache.isis.core.metamodel.facets.collections.CollectionFacet;
 import org.apache.isis.core.metamodel.facets.collections.collection.defaultview.DefaultViewFacet;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.Rel;
@@ -68,7 +69,7 @@ public class ObjectCollectionReprRenderer extends AbstractObjectMemberReprRender
         final LinkFollowSpecs followValue = getLinkFollowSpecs().follow("value");
         boolean eagerlyRender = resourceContext.honorUiHints() && renderEagerly() || !followValue.isTerminated();
 
-        if ((mode.isInline() && eagerlyRender) || mode.isStandalone() || mode.isMutated() || mode.isEventSerialization() || !ManagedObject.isIdentifiable(objectAdapter)) {
+        if ((mode.isInline() && eagerlyRender) || mode.isStandalone() || mode.isMutated() || mode.isEventSerialization() || !ManagedObjects.isIdentifiable(objectAdapter)) {
             addValue(followValue);
         }
         if(!mode.isEventSerialization()) {
diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/context/ResourceContext.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/context/ResourceContext.java
index 809940c..db5a355 100644
--- a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/context/ResourceContext.java
+++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/context/ResourceContext.java
@@ -39,6 +39,7 @@ import org.apache.isis.core.metamodel.adapter.oid.Oid;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.runtime.context.RuntimeContextBase;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
@@ -218,7 +219,7 @@ public class ResourceContext extends RuntimeContextBase implements IResourceCont
     private Set<Oid> rendered = _Sets.newHashSet();
     @Override
     public boolean canEagerlyRender(ManagedObject objectAdapter) {
-        final Oid oid = ManagedObject.identify(objectAdapter).orElse(null);
+        final Oid oid = ManagedObjects.identify(objectAdapter).orElse(null);
         return (oid!=null) 
                 ? rendered.add(oid)
                 : true;
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
index 7affbd5..0d2b752 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
@@ -52,6 +52,7 @@ import org.apache.isis.core.metamodel.facets.object.bookmarkpolicy.BookmarkPolic
 import org.apache.isis.core.metamodel.facets.object.promptStyle.PromptStyleFacet;
 import org.apache.isis.core.metamodel.interactions.InteractionHead;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 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;
@@ -422,14 +423,14 @@ implements FormUiModel, FormExecutorContext, BookmarkableModel {
             val paramValue = actionArgumentModel.getValue();
             val hasChoices = actionParameter.hasChoices();
             val hasAutoComplete = actionParameter.hasAutoComplete();
-            val isEmpty = ManagedObject.isNullOrUnspecifiedOrEmpty(paramValue);
+            val isEmpty = ManagedObjects.isNullOrUnspecifiedOrEmpty(paramValue);
             // if we have choices or autoSelect, don't override any param value, already chosen by the user
             val vetoDefaultsToBeSet = !isEmpty 
                     && (hasChoices||hasAutoComplete);
             
             if(!vetoDefaultsToBeSet) {
                 val paramDefaultValue = actionParameter.getDefault(pendingArgs);
-                if (ManagedObject.isNullOrUnspecifiedOrEmpty(paramDefaultValue)) {
+                if (ManagedObjects.isNullOrUnspecifiedOrEmpty(paramDefaultValue)) {
                     clearParameterValue(actionParameter);
                 } else {
                     setParameterValue(actionParameter, paramDefaultValue);
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BookmarkTreeNode.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BookmarkTreeNode.java
index 6ca7473..9c24e21 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BookmarkTreeNode.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BookmarkTreeNode.java
@@ -31,7 +31,7 @@ import org.apache.isis.core.commons.internal.collections._Lists;
 import org.apache.isis.core.metamodel.adapter.oid.Oid;
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.viewer.wicket.model.mementos.PageParameterNames;
@@ -208,7 +208,7 @@ public class BookmarkTreeNode implements Serializable {
             })
             .filter(_NullSafe::isPresent)
             .map(parentAdapter->{
-                final Oid parentOid = ManagedObject.identify(parentAdapter).orElse(null);
+                final Oid parentOid = ManagedObjects.identify(parentAdapter).orElse(null);
                 return parentOid;
             })
             .filter(_NullSafe::isPresent)
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java
index 293a5f9..991866b 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java
@@ -30,6 +30,7 @@ import org.apache.isis.core.commons.internal.collections._Collections;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facets.object.bookmarkpolicy.BookmarkPolicyFacet;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.webapp.context.IsisWebAppCommonContext;
@@ -75,7 +76,7 @@ extends ModelAbstract<ManagedObject> {
     @Override
     public void setObject(final ManagedObject adapter) {
 
-        if(ManagedObject.isNullOrUnspecifiedOrEmpty(adapter)) {
+        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)) {
             super.setObject(null);
             memento = null;
             return;
@@ -92,7 +93,7 @@ extends ModelAbstract<ManagedObject> {
     
     public void setObjectCollection(final ManagedObject adapter) {
         
-        if(ManagedObject.isNullOrUnspecifiedOrEmpty(adapter)) {
+        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)) {
             super.setObject(null);
             memento = null;
             return;
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/PageParameterUtil.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/PageParameterUtil.java
index e4c1ecd..b78b873 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/PageParameterUtil.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/PageParameterUtil.java
@@ -32,6 +32,7 @@ import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
 import org.apache.isis.core.metamodel.spec.ActionType;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
@@ -68,10 +69,10 @@ public class PageParameterUtil {
     public static PageParameters createPageParametersForObject(ManagedObject adapter) {
 
         val pageParameters = PageParametersUtils.newPageParameters();
-        val isEntity = ManagedObject.isIdentifiable(adapter);
+        val isEntity = ManagedObjects.isIdentifiable(adapter);
 
         if (isEntity) {
-            ManagedObject.stringify(adapter)
+            ManagedObjects.stringify(adapter)
             .ifPresent(oidStr->PageParameterNames.OBJECT_OID.addStringTo(pageParameters, oidStr));
         } else {
             // don't do anything; instead the page should be redirected back to
@@ -103,7 +104,7 @@ public class PageParameterUtil {
 
         val pageParameters = PageParametersUtils.newPageParameters();
 
-        ManagedObject.stringify(adapter)
+        ManagedObjects.stringify(adapter)
         .ifPresent(oidStr->
             PageParameterNames.OBJECT_OID.addStringTo(pageParameters, oidStr));
 
@@ -207,7 +208,7 @@ public class PageParameterUtil {
             return encodeable.toEncodedString(adapter);
         }
 
-        return ManagedObject.stringify(adapter).orElse(null);
+        return ManagedObjects.stringify(adapter).orElse(null);
     }
     
     private ManagedObject decodeArg(
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarPropertyModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarPropertyModel.java
index f38db89..17aba4b 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarPropertyModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarPropertyModel.java
@@ -129,7 +129,7 @@ implements PropertyUiModel {
         val where = this.getRenderingHint().asWhere();
         val propertyValue = getManagedProperty().getPropertyValue(where);
         
-        val presentationValue = ManagedObject.isNullOrUnspecifiedOrEmpty(propertyValue)
+        val presentationValue = ManagedObjects.isNullOrUnspecifiedOrEmpty(propertyValue)
                 ? null
                 : propertyValue;
         
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/EntityActionLinkFactory.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/EntityActionLinkFactory.java
index 385f790..4313bef 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/EntityActionLinkFactory.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/EntityActionLinkFactory.java
@@ -19,7 +19,7 @@
 
 package org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions;
 
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
@@ -48,7 +48,7 @@ public final class EntityActionLinkFactory extends LinkAndLabelFactoryAbstract {
 
         val objectAdapter = this.targetEntityModel.getManagedObject();
 
-        val isIdentifiable = ManagedObject.isIdentifiable(objectAdapter);
+        val isIdentifiable = ManagedObjects.isIdentifiable(objectAdapter);
         if (!isIdentifiable) {
             throw new IllegalArgumentException(String.format(
                     "Object '%s' is not identifiable (has no identifier).", 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterTitleColumn.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterTitleColumn.java
index 8e49d15..0b6b459 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterTitleColumn.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterTitleColumn.java
@@ -25,6 +25,7 @@ import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.model.IModel;
 
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.webapp.context.IsisWebAppCommonContext;
 import org.apache.isis.core.webapp.context.memento.ObjectMemento;
 import org.apache.isis.viewer.common.model.object.ObjectUiModel.RenderingHint;
@@ -70,7 +71,7 @@ public class ObjectAdapterTitleColumn extends ColumnAbstract<ManagedObject> {
     private Component createComponent(final String id, final IModel<ManagedObject> rowModel) {
         val adapter = rowModel.getObject();
         
-        if(ManagedObject.isValue(adapter)) {
+        if(ManagedObjects.isValue(adapter)) {
             val valueModel = new ValueModel(super.getCommonContext(), adapter);
             
             val componentFactory = findComponentFactory(ComponentType.VALUE, valueModel);
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 8e4f140..46b5db6 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
@@ -47,7 +47,7 @@ import org.apache.isis.core.commons.internal.collections._Lists;
 import org.apache.isis.core.metamodel.facets.all.named.NamedFacet;
 import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.labelat.LabelAtFacet;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.viewer.common.model.action.form.FormPendingParamUiModel;
@@ -170,7 +170,7 @@ implements ScalarModelSubscriber2 {
         val valueChanged = !Objects.equals(scalarModel.getObject(), paramValue); 
         
         if(valueChanged) {
-            if(ManagedObject.isNullOrUnspecifiedOrEmpty(paramValue)) {
+            if(ManagedObjects.isNullOrUnspecifiedOrEmpty(paramValue)) {
                 scalarModel.setObject(null);
             } else {
                 scalarModel.setObject(paramValue);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
index 4b51f6b..856fc2a 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
@@ -48,6 +48,7 @@ import org.apache.isis.core.commons.internal.collections._Lists;
 import org.apache.isis.core.commons.internal.functions._Functions;
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.webapp.context.IsisWebAppCommonContext;
 import org.apache.isis.viewer.wicket.model.common.CommonContextUtils;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
@@ -401,7 +402,7 @@ class IsisToWicketTreeAdapter {
         public LoadableDetachableTreeModel(TreeModel tModel) {
             super(tModel);
             this.treePath = tModel.getTreePath();
-            this.id = ManagedObject.identifyElseFail(tModel.getObject());
+            this.id = ManagedObjects.identifyElseFail(tModel.getObject());
                     
             this.hashCode = Objects.hash(id.hashCode(), treePath.hashCode());
             this.commonContext = tModel.getCommonContext();
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/unknown/UnknownModelPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/unknown/UnknownModelPanel.java
index 09944f0..105cd04 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/unknown/UnknownModelPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/unknown/UnknownModelPanel.java
@@ -23,7 +23,7 @@ import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 
@@ -59,7 +59,7 @@ public class UnknownModelPanel extends PanelAbstract<IModel<?>> {
             EntityModel entityModel = (EntityModel) model;
             val objectAdapter = entityModel.getObject();
             if(objectAdapter != null) {
-                buf.append("??? objectAdapter oid: " + ManagedObject.identify(objectAdapter).orElse(null));    
+                buf.append("??? objectAdapter oid: " + ManagedObjects.identify(objectAdapter).orElse(null));    
             } else {
                 buf.append("??? objectAdapter is NULL");
             }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/home/HomePage.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/home/HomePage.java
index 6cc61f0..a66ab45 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/home/HomePage.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/home/HomePage.java
@@ -24,7 +24,7 @@ import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 
 import org.apache.isis.applib.services.message.MessageService;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.components.widgets.breadcrumbs.BreadcrumbModelProvider;
 import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
@@ -59,7 +59,7 @@ public class HomePage extends PageAbstract {
 
         val homePageAdapter = super.getCommonContext().getHomePageAdapter();
 
-        if(ManagedObject.isSpecified(homePageAdapter)) {
+        if(ManagedObjects.isSpecified(homePageAdapter)) {
             val requestCycle = RequestCycle.get();
             requestCycle.setResponsePage(new EntityPage(getCommonContext(), homePageAdapter));
 
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapter.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapter.java
index 2942643..f311abf 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapter.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapter.java
@@ -30,6 +30,7 @@ import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
 import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 
 import lombok.val;
 
@@ -69,12 +70,12 @@ public class ConverterForObjectAdapter implements IConverter<ManagedObject> {
     @Override
     public String convertToString(final ManagedObject adapter, final Locale locale) {
         
-        if(!ManagedObject.isIdentifiable(adapter)) {
+        if(!ManagedObjects.isIdentifiable(adapter)) {
             // eg. values don't have an Oid
             return null;
         }
         
-        return ManagedObject.stringify(adapter)
+        return ManagedObjects.stringify(adapter)
                 .orElse(null);
     }
     
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapterMemento.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapterMemento.java
index 435c053..0a7df37 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapterMemento.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapterMemento.java
@@ -26,7 +26,7 @@ import org.apache.wicket.util.convert.IConverter;
 import org.apache.isis.core.commons.internal.base._Strings;
 import org.apache.isis.core.metamodel.adapter.oid.Oid;
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.webapp.context.IsisWebAppCommonContext;
 import org.apache.isis.core.webapp.context.memento.ObjectMemento;
 
@@ -72,7 +72,7 @@ public class ConverterForObjectAdapterMemento implements IConverter<ObjectMement
         if(spec!=null && spec.isValue()) {
             return memento.toString();
         }
-        return ManagedObject.stringifyElseFail(adapter);
+        return ManagedObjects.stringifyElseFail(adapter);
     }
 
 }
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoLegacy.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoLegacy.java
index 1de452e..c1a58bb 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoLegacy.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoLegacy.java
@@ -34,6 +34,7 @@ import org.apache.isis.core.metamodel.adapter.oid.Oid;
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
@@ -54,7 +55,7 @@ final class ObjectMementoLegacy implements Serializable {
      * Factory method
      */
     public static ObjectMementoLegacy createOrNull(ManagedObject adapter) {
-        if(ManagedObject.isNullOrUnspecifiedOrEmpty(adapter)) {
+        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)) {
             return null;
         }
         return new ObjectMementoLegacy(adapter);
@@ -245,7 +246,7 @@ final class ObjectMementoLegacy implements Serializable {
                 //XXX REVIEW: this may be redundant because recreateAdapter also guarantees the version will be reset.
                 ManagedObject adapter = recreateObject(memento, specificationLoader);
 
-                memento.persistentOidStr = ManagedObject.stringifyElseFail(adapter);
+                memento.persistentOidStr = ManagedObjects.stringifyElseFail(adapter);
             }
 
             @Override
@@ -396,7 +397,7 @@ final class ObjectMementoLegacy implements Serializable {
             return;
         }
 
-        val rootOid = ManagedObject.identifyElseFail(adapter);
+        val rootOid = ManagedObjects.identifyElseFail(adapter);
         persistentOidStr = rootOid.enString();
         bookmark = rootOid.asBookmark();
         if(adapter.getPojo() instanceof HintStore.HintIdProvider) {
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoServiceWicket.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoServiceWicket.java
index c3d9d61..dc985e1 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoServiceWicket.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoServiceWicket.java
@@ -37,6 +37,7 @@ import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.webapp.context.memento.ObjectMemento;
@@ -75,7 +76,7 @@ public class ObjectMementoServiceWicket implements ObjectMementoService {
         val mementoAdapter = ObjectMementoLegacy.createOrNull(adapter);
         if(mementoAdapter==null) {
             // sonar-ignore-on (fails to detect this as null guard) 
-            return ManagedObject.isSpecified(adapter)
+            return ManagedObjects.isSpecified(adapter)
                     ? new ObjectMementoForEmpty(adapter.getSpecification().getSpecId())
                     : null;
             // sonar-ignore-on