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/08/31 05:22:43 UTC

[isis] branch master updated: ISIS-3167: activates ManagedObject for SERVICE

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 984e96027d ISIS-3167: activates ManagedObject for SERVICE
984e96027d is described below

commit 984e96027db69918f61192a75c767c44b4ca2863
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Aug 31 07:22:35 2022 +0200

    ISIS-3167: activates ManagedObject for SERVICE
    
    - removes a lot of MM test mockery
    - also fixes some bean classifier inconsistencies
---
 .../isis/applib/services/metamodel/BeanSort.java   |   4 +-
 .../isis/core/config/beans/IsisBeanMetaData.java   |  31 ++++--
 .../beans/IsisBeanTypeClassifierDefault.java       |  30 ++++--
 .../_testing/MetaModelContext_forTesting.java      |   1 -
 .../facets/object/title/TitleRenderRequest.java    |   6 ++
 .../isis/core/metamodel/object/ManagedObject.java  |  47 +++++----
 .../core/metamodel/object/MmAssertionUtil.java     |   6 +-
 .../core/metamodel/object/_InternalTitleUtil.java  |  16 +--
 .../metamodel/object/_ManagedObjectService.java    |  89 ++++++++++++++++
 .../metamodel/object/_ManagedObjectSpecified.java  |   7 ++
 .../object/_ManagedObjectUnspecified.java          |   5 +
 .../object/_ManagedObjectWithEagerSpec.java        |  10 ++
 .../ClassSubstitutorForDomainObjects.java          |   6 +-
 .../specloader/SpecificationLoaderDefault.java     |   2 +-
 .../specimpl/ObjectSpecificationAbstract.java      |  11 +-
 .../specimpl/dflt/ObjectSpecificationDefault.java  |   2 +-
 .../facets/AbstractFacetFactoryJUnit4TestCase.java |   7 +-
 .../action/ActionAnnotationFacetFactoryTest.java   |   6 --
 ...nLayoutXmlLayoutAnnotationFacetFactoryTest.java |  56 ----------
 ...MenuFacetFromDomainServiceFacetFactoryTest.java |  40 --------
 .../CollectionAnnotationFacetFactoryTest.java      |  10 +-
 .../title/TitleAnnotationFacetFactoryTest.java     | 114 +++------------------
 .../ParameterAnnotationFacetFactoryTest.java       |  12 +--
 .../PropertyAnnotationFacetFactoryTest.java        |  41 --------
 .../core/metamodel/object/ManagedObjectTest.java   |  89 ++++++++++++++++
 .../runtimeservices/memento/_ObjectMemento.java    |   2 +-
 .../isis/viewer/wicket/ui/pages/PageAbstract.java  |   2 +-
 .../choices/ChoiceProviderForValuesTest.java       |   3 +-
 28 files changed, 332 insertions(+), 323 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/BeanSort.java b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/BeanSort.java
index 46a98be23b..be968d75dd 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/BeanSort.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/BeanSort.java
@@ -93,7 +93,7 @@ public enum BeanSort {
         return this == MANAGED_BEAN_NOT_CONTRIBUTING;
     }
 
-    public boolean isManagedBean() {
+    public boolean isManagedBeanAny() {
         return this == MANAGED_BEAN_CONTRIBUTING
                 || this == MANAGED_BEAN_NOT_CONTRIBUTING;
     }
@@ -146,7 +146,7 @@ public enum BeanSort {
     }
 
     public boolean isWrappingSupported() {
-        return isMixin() || isViewModel() || isEntity() || isManagedBean();
+        return isMixin() || isViewModel() || isEntity() || isManagedBeanAny();
     }
 
 
diff --git a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanMetaData.java b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanMetaData.java
index 547e38a847..863f1028ee 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanMetaData.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanMetaData.java
@@ -28,27 +28,27 @@ import lombok.Value;
 public class IsisBeanMetaData {
 
     public enum ManagedBy {
-        UNSPECIFIED,
+        NONE,
         ISIS,
         SPRING,
+        /** other Spring managed component, or not managed at all */
+        INDIFFERENT,
         /** @deprecated in support of deprecated {@code @DomainService(logicalTypeName=...)}*/
         @Deprecated
         SPRING_NAMED_BY_ISIS,
         ;
-        public boolean isUnspecified() {return this == UNSPECIFIED; }
+        public boolean isNone() { return this == NONE; }
         public boolean isIsis() { return this == ISIS; }
-        public boolean isSpring() {
-            return this == SPRING
-                || this == SPRING_NAMED_BY_ISIS;
-        }
+        public boolean isSpring() { return this == SPRING; }
+
         /**
          * Whether Spring should make that underlying bean injectable.
          * @implNote if not managed by Isis, let ultimately Spring decide
          */
         public boolean isInjectable() {
-            return !isIsis();
+            return !isIsis()
+                    && !isNone();
         }
-
         /**
          * Whether we interfere with Spring's naming strategy.
          */
@@ -72,6 +72,18 @@ public class IsisBeanMetaData {
 
     // -- FACTORIES
 
+    public static IsisBeanMetaData notManaged(
+            final @NonNull BeanSort beanSort,
+            final @NonNull LogicalType logicalType) {
+        return of(beanSort, logicalType, ManagedBy.NONE);
+    }
+
+    public static IsisBeanMetaData notManaged(
+            final @NonNull BeanSort beanSort,
+            final @NonNull Class<?> type) {
+        return notManaged(beanSort, LogicalType.infer(type));
+    }
+
     public static IsisBeanMetaData injectable(
             final @NonNull BeanSort beanSort,
             final @NonNull LogicalType logicalType) {
@@ -92,7 +104,8 @@ public class IsisBeanMetaData {
     public static IsisBeanMetaData indifferent(
             final @NonNull BeanSort beanSort,
             final @NonNull Class<?> type) {
-        return of(beanSort, LogicalType.infer(type), ManagedBy.UNSPECIFIED);
+        return of(beanSort, LogicalType.infer(type),
+                ManagedBy.INDIFFERENT);
     }
 
     public static IsisBeanMetaData isisManaged(
diff --git a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierDefault.java b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierDefault.java
index c67d426003..fd4f858626 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierDefault.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierDefault.java
@@ -26,12 +26,14 @@ import javax.persistence.Entity;
 
 import org.springframework.context.annotation.Profile;
 import org.springframework.stereotype.Component;
+import org.springframework.util.ClassUtils;
 
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.id.LogicalType;
 import org.apache.isis.applib.services.metamodel.BeanSort;
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.reflection._Annotations;
 import org.apache.isis.core.config.progmodel.ProgrammingModelConstants.TypeExcludeMarker;
 
@@ -48,15 +50,16 @@ implements IsisBeanTypeClassifier {
     private final Can<String> activeProfiles;
     private final Can<IsisBeanTypeClassifier> classifierPlugins = IsisBeanTypeClassifier.get();
 
+    @SuppressWarnings("deprecation")
     @Override
     public IsisBeanMetaData classify(
             final @NonNull Class<?> type) {
 
         // handle arbitrary types ...
 
-        if(type.isPrimitive()
+        if(ClassUtils.isPrimitiveOrWrapper(type)
                 || type.isEnum()) {
-            return IsisBeanMetaData.indifferent(BeanSort.VALUE, type);
+            return IsisBeanMetaData.notManaged(BeanSort.VALUE, type);
         }
 
         if(Collection.class.isAssignableFrom(type)
@@ -79,7 +82,7 @@ implements IsisBeanTypeClassifier {
 
         // handle vetoing ...
         if(TypeExcludeMarker.anyMatchOn(type)) {
-            return IsisBeanMetaData.isisManaged(BeanSort.VETOED, type); // reject
+            return IsisBeanMetaData.notManaged(BeanSort.VETOED, type); // reject
         }
 
         val profiles = Can.ofArray(_Annotations.synthesize(type, Profile.class)
@@ -87,7 +90,7 @@ implements IsisBeanTypeClassifier {
                 .orElse(null));
         if(profiles.isNotEmpty()
                 && !profiles.stream().anyMatch(this::isProfileActive)) {
-            return IsisBeanMetaData.isisManaged(BeanSort.VETOED, type); // reject
+            return IsisBeanMetaData.notManaged(BeanSort.VETOED, type); // reject
         }
 
         // handle value types ...
@@ -95,7 +98,7 @@ implements IsisBeanTypeClassifier {
         val aValue = _Annotations.synthesize(type, org.apache.isis.applib.annotation.Value.class)
                 .orElse(null);
         if(aValue!=null) {
-            return IsisBeanMetaData.indifferent(BeanSort.VALUE, type);
+            return IsisBeanMetaData.notManaged(BeanSort.VALUE, type);
         }
 
         // handle actual bean types ...
@@ -103,9 +106,20 @@ implements IsisBeanTypeClassifier {
         val aDomainService = _Annotations.synthesize(type, DomainService.class);
         if(aDomainService.isPresent()) {
             val logicalType = LogicalType.infer(type);
-            // overrides Spring naming strategy
-            return IsisBeanMetaData
-                        .injectableNamedByIsis(BeanSort.MANAGED_BEAN_CONTRIBUTING, logicalType);
+
+            // whether overrides Spring naming strategy
+            @SuppressWarnings("removal")
+            val namedByIsis = aDomainService
+                    .map(DomainService::logicalTypeName)
+                    .map(_Strings::emptyToNull)
+                    .map(logicalType.getLogicalTypeName()::equals)
+                    .orElse(false);
+
+            return namedByIsis
+                    ? IsisBeanMetaData
+                        .injectableNamedByIsis(BeanSort.MANAGED_BEAN_CONTRIBUTING, logicalType)
+                    : IsisBeanMetaData
+                        .injectable(BeanSort.MANAGED_BEAN_CONTRIBUTING, logicalType);
         }
 
         // allow ServiceLoader plugins to have a say, eg. when classifying entity types
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java
index b8864ec3ff..b8665866a6 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java
@@ -503,7 +503,6 @@ implements MetaModelContext {
     }
 
     private final Optional<ManagedObject> toManagedObject(final _ManagedBeanAdapter managedBeanAdapter) {
-
         val servicePojo = managedBeanAdapter.getInstance().getFirst()
                 .orElseThrow(()->_Exceptions.unrecoverable(
                         "Cannot get service instance of type '%s'",
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/TitleRenderRequest.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/TitleRenderRequest.java
index 3b4c6c997a..14b5ec6104 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/TitleRenderRequest.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/TitleRenderRequest.java
@@ -33,6 +33,12 @@ import lombok.Value;
 @Value @Builder
 public class TitleRenderRequest {
 
+    public static TitleRenderRequest forObject(final ManagedObject object) {
+        return TitleRenderRequest.builder()
+        .object(object)
+        .build();
+    }
+
     private final @Nullable ObjectFeature feature;
     private final @NonNull ManagedObject object;
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
index 4ba7466d57..81431173cf 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
@@ -306,7 +306,6 @@ public interface ManagedObject extends HasMetaModelContext {
     /**
      * Returns the specific {@link Specialization} this {@link ManagedObject} implements,
      * which governs this object's behavior.
-     * @implNote FIXME[ISIS-3167] not fully implemented yet
      */
     Specialization getSpecialization();
 
@@ -356,7 +355,17 @@ public interface ManagedObject extends HasMetaModelContext {
 
     // -- TITLE
 
-    public default String titleString(final UnaryOperator<TitleRenderRequest.TitleRenderRequestBuilder> onBuilder) {
+    /**
+     * The (TODO translated ?) title of the wrapped pojo.
+     */
+    String getTitle();
+
+    @Deprecated
+    default String titleString() {
+        return getTitle();
+    }
+
+    default String titleString(final UnaryOperator<TitleRenderRequest.TitleRenderRequestBuilder> onBuilder) {
         return _InternalTitleUtil
                 .titleString(onBuilder.apply(
                         TitleRenderRequest.builder()
@@ -364,12 +373,6 @@ public interface ManagedObject extends HasMetaModelContext {
                         .build());
     }
 
-    public default String titleString() {
-        return _InternalTitleUtil.titleString(
-                TitleRenderRequest.builder()
-                .object(this)
-                .build());
-    }
 
     // -- SHORTCUT - ELEMENT SPECIFICATION
 
@@ -380,12 +383,6 @@ public interface ManagedObject extends HasMetaModelContext {
         return getSpecification().getElementSpecification();
     }
 
-    // -- SHORTCUT - TITLE
-
-    default String getTitle() {
-        return MmTitleUtil.titleOf(this);
-    }
-
     // -- SHORTCUT - ICON
 
     /**
@@ -434,7 +431,6 @@ public interface ManagedObject extends HasMetaModelContext {
             final @NonNull ObjectSpecification spec,
             final @Nullable Object pojo) {
         return pojo != null
-                //? new _ManagedObjectWithEagerSpec(spec, pojo) //FIXME
                 ? new _ManagedObjectValue(spec, pojo)
                 : empty(spec);
     }
@@ -449,7 +445,7 @@ public interface ManagedObject extends HasMetaModelContext {
     static ManagedObject service(
             final @NonNull ObjectSpecification spec,
             final @NonNull Object pojo) {
-        return new _ManagedObjectWithEagerSpec(spec, pojo); //FIXME
+        return new _ManagedObjectService(spec, pojo);
     }
     /**
      * VIEWMODEL
@@ -536,9 +532,17 @@ public interface ManagedObject extends HasMetaModelContext {
         if(pojo instanceof ManagedObject) {
             return (ManagedObject)pojo;
         }
-        _Assert.assertFalse(_Collections.isCollectionOrArrayOrCanType(pojo.getClass()));
-
         val spec = specLoader.specForType(pojo.getClass()).orElse(null);
+        return wrapScalarInternal(spec, pojo);
+    }
+
+    private static ManagedObject wrapScalarInternal(
+            final @Nullable ObjectSpecification spec,
+            final @NonNull Object pojo) {
+
+        _Assert.assertTrue(!_Collections.isCollectionOrArrayOrCanType(pojo.getClass()),
+                ()->String.format("is scalar %s", pojo.getClass()));
+
         val specialization = spec!=null
                 ? Specialization.inferFrom(spec, pojo)
                 : Specialization.UNSPECIFIED;
@@ -572,7 +576,12 @@ public interface ManagedObject extends HasMetaModelContext {
     static ManagedObject notBookmarked(
             final ObjectSpecification spec,
             final Object pojo) {
-        return new _ManagedObjectWithEagerSpec(spec, pojo);
+        if(pojo instanceof ManagedObject) {
+            return (ManagedObject)pojo;
+        }
+        return !_Collections.isCollectionOrArrayOrCanType(pojo.getClass())
+                ? wrapScalarInternal(spec, pojo)
+                : new _ManagedObjectWithEagerSpec(spec, pojo);
     }
 
     /**
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java
index 654b3c805b..b31317dfd3 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java
@@ -35,7 +35,7 @@ import lombok.experimental.UtilityClass;
 @UtilityClass
 public class MmAssertionUtil {
 
-    public static void assertExactType(
+    public void assertExactType(
             final @Nullable ObjectSpecification requiredSpec,
             final @Nullable Object pojo) {
         if(pojo==null
@@ -54,7 +54,7 @@ public class MmAssertionUtil {
     /**
      * Guard against incompatible type.
      */
-    public static @NonNull UnaryOperator<ManagedObject> assertInstanceOf(
+    public @NonNull UnaryOperator<ManagedObject> assertInstanceOf(
             final ObjectSpecification elementType) {
         return object -> {
             if(ManagedObjects.isInstanceOf(object, elementType)) {
@@ -72,7 +72,7 @@ public class MmAssertionUtil {
     /**
      * eg. in order to prevent wrapping an object that is already wrapped
      */
-    public static void assertPojoNotWrapped(final @Nullable Object pojo) {
+    public void assertPojoNotWrapped(final @Nullable Object pojo) {
         // can do this check only when the pojo is not null, otherwise is always considered valid
         if(pojo==null) {
             return;
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_InternalTitleUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_InternalTitleUtil.java
index 3ee4d43c4a..0b8a94f8de 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_InternalTitleUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_InternalTitleUtil.java
@@ -36,16 +36,16 @@ final class _InternalTitleUtil {
 
         val managedObject = titleRenderRequest.getObject();
 
-        if(!ManagedObjects.isSpecified(managedObject)) {
-            return "unspecified object";
+        if(managedObject.getSpecialization().isUnspecified()) {
+            return managedObject.getTitle();
         }
 
-        return managedObject.getSpecification().isNonScalar()
-            ? collectionTitleString(
+        return managedObject.getSpecification().isScalar()
+            ? objectTitleString(titleRenderRequest)
+                    .trim()
+            : collectionTitleString(
                     managedObject,
-                    managedObject.getSpecification().getFacet(CollectionFacet.class))
-            : objectTitleString(titleRenderRequest)
-                .trim();
+                    managedObject.getSpecification().getFacet(CollectionFacet.class));
     }
 
     // -- HELPER
@@ -58,6 +58,8 @@ final class _InternalTitleUtil {
 
     private String objectTitleString(@NonNull final TitleRenderRequest titleRenderRequest) {
         val managedObject = titleRenderRequest.getObject();
+
+        //TODO we have value-semantics now, don't skip it for strings
         if (managedObject.getPojo() instanceof String) {
             return (String) managedObject.getPojo();
         }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectService.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectService.java
new file mode 100644
index 0000000000..aa9b839b9d
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectService.java
@@ -0,0 +1,89 @@
+/*
+ *  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 java.util.function.Supplier;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.commons.internal.assertions._Assert;
+import org.apache.isis.commons.internal.base._Lazy;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.experimental.Accessors;
+
+/**
+ * (package private) specialization corresponding to {@link Specialization#SERVICE}
+ * @see ManagedObject.Specialization#SERVICE
+ */
+@Getter
+final class _ManagedObjectService
+extends _ManagedObjectSpecified {
+
+    @Getter(onMethod_ = {@Override}) @Accessors(makeFinal = true)
+    private final @NonNull Object pojo;
+
+    protected final _Lazy<Optional<Bookmark>> bookmarkLazy =
+            _Lazy.threadSafe(()->Optional.of(createBookmark()));
+
+    _ManagedObjectService(
+            final ObjectSpecification spec,
+            final Object pojo) {
+        super(ManagedObject.Specialization.SERVICE, spec);
+        _Assert.assertTrue(spec.getBeanSort().isManagedBeanAny()
+                || spec.getBeanSort().isUnknown(), ()->
+                String.format("service %s must be a managed bean (or unknown); sort= %s",
+                        pojo.getClass(), spec.getBeanSort()));
+        _Assert.assertTrue(spec.isInjectable(), ()->
+            String.format("service %s must be injectible; sort= %s",
+                    pojo.getClass(), spec.getBeanSort()));
+        this.pojo = assertCompliance(pojo);
+    }
+
+    @Override
+    public Optional<Bookmark> getBookmark() {
+        return bookmarkLazy.get();
+    }
+
+    @Override
+    public Optional<Bookmark> getBookmarkRefreshed() {
+        return getBookmark(); // no-op for services
+    }
+
+    @Override
+    public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) {
+        // no-op for services
+    }
+
+    @Override
+    public boolean isBookmarkMemoized() {
+        return bookmarkLazy.isMemoized();
+    }
+
+    // -- HELPER
+
+    private Bookmark createBookmark() {
+        return Bookmark.forLogicalTypeAndIdentifier(
+                getSpecification().getLogicalType(),
+                "1");
+    }
+
+}
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
index 4894928b96..0dd2265db0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
@@ -25,6 +25,7 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 import lombok.AccessLevel;
@@ -69,6 +70,12 @@ implements ManagedObject {
         return pojo;
     }
 
+    @Override
+    public final String getTitle() {
+        return _InternalTitleUtil.titleString(
+                TitleRenderRequest.forObject(this));
+    }
+
     //XXX compares pojos by their 'equals' semantics -
     // note though: some value-types have an explicit order-relation which could potentially say differently
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
index b8ac3a0ac5..a4992373d1 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
@@ -91,6 +91,11 @@ implements ManagedObject {
         return pojo; // no-op
     }
 
+    @Override
+    public String getTitle() {
+        return "unspecified object";
+    }
+
     @Override
     public boolean equals(final Object other) {
         return Objects.equals(this, other);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java
index 7729755a90..a7fc080e5b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java
@@ -26,6 +26,7 @@ import org.springframework.lang.Nullable;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.collections._Collections;
+import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 import lombok.EqualsAndHashCode;
@@ -59,6 +60,9 @@ extends _ManagedObjectWithBookmark {
             final ObjectSpecification spec,
             final Object pojo) {
         super(ManagedObject.Specialization.inferFrom(spec, pojo));
+        //_Assert.assertFalse(getSpecialization().isValue()); // VALUE already migrated
+        _Assert.assertFalse(getSpecialization().isService()); // SERVICE already migrated
+
         this.specification = spec;
         this.pojo = pojo;
     }
@@ -71,4 +75,10 @@ extends _ManagedObjectWithBookmark {
         pojo = assertCompliance(replacer.apply(pojo));
     }
 
+    @Override
+    public final String getTitle() {
+        return _InternalTitleUtil.titleString(
+                TitleRenderRequest.forObject(this));
+    }
+
 }
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java
index 26b2869af6..664f827686 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java
@@ -39,12 +39,12 @@ public class ClassSubstitutorForDomainObjects implements ClassSubstitutor {
     private IsisBeanTypeRegistry isisBeanTypeRegistry;
 
     @Inject
-    public ClassSubstitutorForDomainObjects(IsisBeanTypeRegistry isisBeanTypeRegistry) {
+    public ClassSubstitutorForDomainObjects(final IsisBeanTypeRegistry isisBeanTypeRegistry) {
         this.isisBeanTypeRegistry = isisBeanTypeRegistry;
     }
 
     @Override
-    public Substitution getSubstitution(@NonNull Class<?> cls) {
+    public Substitution getSubstitution(@NonNull final Class<?> cls) {
 
         val beanSort = isisBeanTypeRegistry.lookupIntrospectableType(cls)
         .map(IsisBeanMetaData::getBeanSort)
@@ -53,7 +53,7 @@ public class ClassSubstitutorForDomainObjects implements ClassSubstitutor {
         if(beanSort!=null) {
             if(beanSort.isEntity()
                     || beanSort.isViewModel()
-                    || beanSort.isManagedBean()) {
+                    || beanSort.isManagedBeanAny()) {
                 return Substitution.neverReplaceClass();
             }
         }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java
index 9ada3880b1..e64b57e123 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java
@@ -264,7 +264,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 
             val sort = typeMeta.getBeanSort();
 
-            if(sort.isManagedBean() || sort.isEntity() || sort.isViewModel() ) {
+            if(sort.isManagedBeanAny() || sort.isEntity() || sort.isViewModel() ) {
                 domainObjectSpecs.add(spec);
             } else if(sort.isMixin()) {
                 mixinSpecs.add(spec);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index bc0767a990..a385325532 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -472,14 +472,21 @@ implements ObjectSpecification {
     public String getSingularName() {
         return lookupFacet(ObjectNamedFacet.class)
             .flatMap(textFacet->textFacet.translated(NounForm.SINGULAR))
-            .orElseGet(this::getFullIdentifier); // unexpected code reach, however keep for JUnit testing
+            // unexpected code reach, however keep for JUnit testing
+            .orElseGet(()->String.format(
+                    "(%s has neither title- nor object-named-facet)",
+                    getFullIdentifier()));
     }
 
     @Override
     public String getPluralName() {
         return lookupFacet(ObjectNamedFacet.class)
             .flatMap(textFacet->textFacet.translated(NounForm.PLURAL))
-            .orElseGet(this::getFullIdentifier); // unexpected code reach, however keep for JUnit testing
+            // unexpected code reach, however keep for JUnit testing
+            .orElseGet(()->String.format(
+                    "(%s has neither title- nor object-named-facet)",
+                    getFullIdentifier()));
+
     }
 
     /**
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java
index 4b67ce3a2b..4e5a97a166 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java
@@ -101,7 +101,7 @@ implements FacetHolder {
                 typeMeta.getLogicalType().getLogicalTypeSimpleName(),
                 typeMeta.getBeanSort(), facetProcessor, postProcessor);
 
-        this.injectable = typeMeta.getManagedBy().isSpring();
+        this.injectable = typeMeta.getManagedBy().isInjectable();
         this.classSubstitutorRegistry = classSubstitutorRegistry;
 
         // must install EncapsulationFacet (if any) and MemberAnnotationPolicyFacet (if any)
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/AbstractFacetFactoryJUnit4TestCase.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/AbstractFacetFactoryJUnit4TestCase.java
index 1ad1aa981e..cd68494c17 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/AbstractFacetFactoryJUnit4TestCase.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/AbstractFacetFactoryJUnit4TestCase.java
@@ -42,6 +42,7 @@ import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneActionParameter;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.valuesemantics.IntValueSemantics;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -54,7 +55,7 @@ implements HasMetaModelContext {
 
     @Mock protected MethodRemover mockMethodRemover;
     @Mock protected FacetHolder mockFacetHolder;
-    @Mock protected SpecificationLoader mockSpecificationLoader;
+    protected SpecificationLoader specificationLoader;
 
     @Mock protected ObjectSpecification mockOnType;
     @Mock protected ObjectSpecification mockObjSpec;
@@ -76,9 +77,11 @@ implements HasMetaModelContext {
     public void setUpFacetedMethodAndParameter() throws Exception {
 
         metaModelContext = MetaModelContext_forTesting.builder()
-                        .specificationLoader(mockSpecificationLoader)
+                        .valueSemantic(new IntValueSemantics())
                         .build();
 
+        specificationLoader = metaModelContext.getSpecificationLoader();
+
         facetHolder = FacetHolder.simple(
                 metaModelContext,
                 Identifier.propertyIdentifier(LogicalType.fqcn(Customer.class), "firstName"));
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java
index 36e51284ce..d78c71a7bd 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java
@@ -50,13 +50,7 @@ extends AbstractFacetFactoryJUnit4TestCase {
     }
 
     void allowingLoadSpecificationRequestsFor(final Class<?> cls, final Class<?> returnType) {
-        context.checking(new Expectations() {{
-            allowing(mockSpecificationLoader).loadSpecification(cls);
-            will(returnValue(mockTypeSpec));
 
-            allowing(mockSpecificationLoader).loadSpecification(returnType);
-            will(returnValue(mockReturnTypeSpec));
-        }});
     }
 
     @Before
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutXmlLayoutAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutXmlLayoutAnnotationFacetFactoryTest.java
index 5e96f47dd6..c7d24c0133 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutXmlLayoutAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutXmlLayoutAnnotationFacetFactoryTest.java
@@ -18,7 +18,6 @@ package org.apache.isis.core.metamodel.facets.actions.layout;
 
 import java.lang.reflect.Method;
 
-import org.jmock.Expectations;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -37,8 +36,6 @@ import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext;
 import org.apache.isis.core.metamodel.facets.actions.position.ActionPositionFacet;
 import org.apache.isis.core.metamodel.facets.actions.position.ActionPositionFacetFallback;
 import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaFacet;
-import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacet;
-import org.apache.isis.core.metamodel.facets.object.mixin.MixinFacet;
 
 public class ActionLayoutXmlLayoutAnnotationFacetFactoryTest
 extends AbstractFacetFactoryJUnit4TestCase {
@@ -62,19 +59,6 @@ extends AbstractFacetFactoryJUnit4TestCase {
 
         final Method method = findMethod(Customer.class, "foz");
 
-        context.checking(new Expectations() {
-            {
-                allowing(mockSpecificationLoader).loadSpecification(Customer.class);
-                will(returnValue(mockObjSpec));
-
-                allowing(mockObjSpec).getFacet(MixinFacet.class);
-                will(returnValue(null));
-
-                allowing(mockObjSpec).getFacet(DomainServiceFacet.class);
-                will(returnValue(null));
-            }
-        });
-
         facetFactory.process(ProcessMethodContext.forTesting(Customer.class, null, method, mockMethodRemover,
                 facetedMethod));
 
@@ -97,20 +81,6 @@ extends AbstractFacetFactoryJUnit4TestCase {
         }
         final Method method = findMethod(Customer.class, "foo");
 
-        context.checking(new Expectations() {
-            {
-                allowing(mockSpecificationLoader).loadSpecification(Customer.class);
-                will(returnValue(mockObjSpec));
-
-                allowing(mockObjSpec).getFacet(MixinFacet.class);
-                will(returnValue(null));
-
-                allowing(mockObjSpec).getFacet(DomainServiceFacet.class);
-                will(returnValue(null));
-
-            }
-        });
-
         facetFactory.process(ProcessMethodContext.forTesting(Customer.class, null, method, mockMethodRemover,
                 facetedMethod));
 
@@ -132,19 +102,6 @@ extends AbstractFacetFactoryJUnit4TestCase {
             }
             final Method method = findMethod(Customer.class, "foz");
 
-            context.checking(new Expectations() {
-                {
-                    allowing(mockSpecificationLoader).loadSpecification(Customer.class);
-                    will(returnValue(mockObjSpec));
-
-                    allowing(mockObjSpec).getFacet(DomainServiceFacet.class);
-                    will(returnValue(null));
-
-                    allowing(mockObjSpec).getFacet(MixinFacet.class);
-                    will(returnValue(null));
-                }
-            });
-
             facetFactory.process(ProcessMethodContext
                     .forTesting(Customer.class, null, method, mockMethodRemover, facetedMethod));
 
@@ -167,19 +124,6 @@ extends AbstractFacetFactoryJUnit4TestCase {
             }
             final Method method = findMethod(Customer.class, "foz");
 
-            context.checking(new Expectations() {
-                {
-                    allowing(mockSpecificationLoader).loadSpecification(Customer.class);
-                    will(returnValue(mockObjSpec));
-
-                    allowing(mockObjSpec).getFacet(DomainServiceFacet.class);
-                    will(returnValue(null));
-
-                    allowing(mockObjSpec).getFacet(MixinFacet.class);
-                    will(returnValue(null));
-                }
-            });
-
             facetFactory.process(ProcessMethodContext
                     .forTesting(Customer.class, null, method, mockMethodRemover, facetedMethod));
 
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/notinservicemenu/derived/NotInServiceMenuFacetFromDomainServiceFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/notinservicemenu/derived/NotInServiceMenuFacetFromDomainServiceFacetFactoryTest.java
index d9ad7f5c73..c595581d80 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/notinservicemenu/derived/NotInServiceMenuFacetFromDomainServiceFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/notinservicemenu/derived/NotInServiceMenuFacetFromDomainServiceFacetFactoryTest.java
@@ -18,9 +18,6 @@
  */
 package org.apache.isis.core.metamodel.facets.actions.notinservicemenu.derived;
 
-import java.util.Optional;
-
-import org.jmock.Expectations;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -37,8 +34,6 @@ import org.apache.isis.core.metamodel.facets.AbstractFacetFactoryJUnit4TestCase;
 import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
 import org.apache.isis.core.metamodel.facets.actions.notinservicemenu.NotInServiceMenuFacet;
-import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacet;
-import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacetAbstract;
 
 @SuppressWarnings("unused")
 public class NotInServiceMenuFacetFromDomainServiceFacetFactoryTest
@@ -63,15 +58,6 @@ extends AbstractFacetFactoryJUnit4TestCase {
             }
         }
 
-        context.checking(new Expectations() {{
-            allowing(mockSpecificationLoader).loadSpecification(CustomerService.class);
-            will(returnValue(mockObjSpec));
-
-            allowing(mockObjSpec).lookupNonFallbackFacet(DomainServiceFacet.class);
-            will(returnValue(Optional.of(new DomainServiceFacetAbstract(mockObjSpec, NatureOfService.REST) {
-            })));
-        }});
-
         expectNoMethodsRemoved();
 
         facetedMethod = FacetedMethod
@@ -101,15 +87,6 @@ extends AbstractFacetFactoryJUnit4TestCase {
             }
         }
 
-        context.checking(new Expectations() {{
-            allowing(mockSpecificationLoader).loadSpecification(CustomerService.class);
-            will(returnValue(mockObjSpec));
-
-            allowing(mockObjSpec).lookupNonFallbackFacet(DomainServiceFacet.class);
-            will(returnValue(Optional.of(new DomainServiceFacetAbstract(mockObjSpec, NatureOfService.VIEW) {
-            })));
-        }});
-
         expectNoMethodsRemoved();
 
         facetedMethod = FacetedMethod
@@ -136,15 +113,6 @@ extends AbstractFacetFactoryJUnit4TestCase {
             }
         }
 
-        context.checking(new Expectations() {{
-            allowing(mockSpecificationLoader).loadSpecification(CustomerService.class);
-            will(returnValue(mockObjSpec));
-
-            allowing(mockObjSpec).lookupNonFallbackFacet(DomainServiceFacet.class);
-            will(returnValue(Optional.of(new DomainServiceFacetAbstract(mockObjSpec, NatureOfService.VIEW) {
-            })));
-        }});
-
         expectNoMethodsRemoved();
 
         facetedMethod = FacetedMethod
@@ -170,14 +138,6 @@ extends AbstractFacetFactoryJUnit4TestCase {
             }
         }
 
-        context.checking(new Expectations() {{
-            allowing(mockSpecificationLoader).loadSpecification(CustomerService.class);
-            will(returnValue(mockObjSpec));
-
-            allowing(mockObjSpec).lookupNonFallbackFacet(DomainServiceFacet.class);
-            will(returnValue(Optional.empty()));
-        }});
-
         expectNoMethodsRemoved();
 
         facetedMethod = FacetedMethod
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactoryTest.java
index 8144d7e99c..5b81af2e42 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactoryTest.java
@@ -66,16 +66,8 @@ extends AbstractFacetFactoryJUnit4TestCase {
         });
     }
 
+    @Deprecated
     void allowingLoadSpecificationRequestsFor(final Class<?> cls, final Class<?> returnType) {
-        context.checking(new Expectations() {
-            {
-                allowing(mockSpecificationLoader).loadSpecification(cls);
-                will(returnValue(mockTypeSpec));
-
-                allowing(mockSpecificationLoader).loadSpecification(returnType);
-                will(returnValue(mockReturnTypeSpec));
-            }
-        });
     }
 
     private static void processModify(
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java
index 4de5c842e9..dc2e7c70d3 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java
@@ -21,10 +21,7 @@ package org.apache.isis.core.metamodel.facets.object.ident.title;
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Optional;
 
-import org.jmock.Expectations;
-import org.jmock.auto.Mock;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -32,6 +29,7 @@ import org.junit.Test;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import org.apache.isis.applib.annotation.Title;
 import org.apache.isis.core.metamodel.facetapi.Facet;
@@ -39,28 +37,20 @@ import org.apache.isis.core.metamodel.facets.AbstractFacetFactoryJUnit4TestCase;
 import org.apache.isis.core.metamodel.facets.Evaluators;
 import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessClassContext;
 import org.apache.isis.core.metamodel.facets.object.title.TitleFacet;
-import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest;
 import org.apache.isis.core.metamodel.facets.object.title.annotation.TitleAnnotationFacetFactory;
 import org.apache.isis.core.metamodel.facets.object.title.annotation.TitleFacetViaTitleAnnotation;
 import org.apache.isis.core.metamodel.object.ManagedObject;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+
+import lombok.val;
 
 public class TitleAnnotationFacetFactoryTest
 extends AbstractFacetFactoryJUnit4TestCase {
 
     private TitleAnnotationFacetFactory facetFactory;
 
-    @Mock private ManagedObject mockObjectAdapter;
-    @Mock private ObjectSpecification mockStringSpec;
-    @Mock private ObjectSpecification mockIntegerSpec;
-
     @Before
     public void setUp() throws Exception {
-
-        // PRODUCTION
-
         facetFactory = new TitleAnnotationFacetFactory(metaModelContext);
-
     }
 
     @After
@@ -142,34 +132,9 @@ extends AbstractFacetFactoryJUnit4TestCase {
         }
 
         final Customer2 customer = new Customer2();
+        val objectAdapter = ManagedObject.wrapScalar(specificationLoader, customer);
 
-        context.checking(new Expectations() {{
-
-            allowing(mockObjectAdapter).getPojo();
-            will(returnValue(customer));
-
-            allowing(mockSpecificationLoader).specForType(String.class);
-            will(returnValue(Optional.of(mockStringSpec)));
-
-            allowing(mockStringSpec).getCorrespondingClass();
-            will(returnValue(String.class));
-
-            allowing(mockStringSpec).isValue();
-            will(returnValue(true));
-
-            allowing(mockStringSpec).isScalar();
-            will(returnValue(true));
-
-            allowing(mockStringSpec).isNonScalar();
-            will(returnValue(false));
-
-            allowing(mockStringSpec).isEntity();
-            will(returnValue(false));
-
-            ignoring(mockStringSpec).assertPojoCompatible(with(any(String.class)));
-
-        }});
-        final String title = titleFacetViaTitleAnnotation.title(mockObjectAdapter);
+        final String title = titleFacetViaTitleAnnotation.title(objectAdapter);
         assertThat(title, is("titleElement1. titleElement3,titleElement2"));
     }
 
@@ -232,70 +197,21 @@ extends AbstractFacetFactoryJUnit4TestCase {
     @Test
     public void titleAnnotatedMethodsSomeOfWhichReturnNulls() throws Exception {
 
+        { // check prerequisites
+            val wThree = ManagedObject.wrapScalar(specificationLoader, Integer.valueOf(3));
+            assertEquals("3", wThree.getTitle());
+            val pThree = ManagedObject.wrapScalar(specificationLoader, 3);
+            assertEquals("3", pThree.getTitle());
+        }
+
         facetFactory.process(ProcessClassContext
                 .forTesting(Customer4.class, mockMethodRemover, facetedMethod));
 
-        final Facet facet = facetedMethod.getFacet(TitleFacet.class);
-        final TitleFacetViaTitleAnnotation titleFacetViaTitleAnnotation = (TitleFacetViaTitleAnnotation) facet;
-
         final Customer4 customer = new Customer4();
+        val objectAdapter = ManagedObject.wrapScalar(specificationLoader, customer);
 
-        context.checking(new Expectations() {
-            {
-                allowing(mockObjectAdapter).getPojo();
-                will(returnValue(customer));
-
-                allowing(mockSpecificationLoader).specForType(String.class);
-                will(returnValue(Optional.of(mockStringSpec)));
-
-                allowing(mockStringSpec).getCorrespondingClass();
-                will(returnValue(String.class));
-
-                allowing(mockStringSpec).isNonScalar();
-                will(returnValue(false));
-
-                allowing(mockStringSpec).isScalar();
-                will(returnValue(true));
-
-                allowing(mockStringSpec).isEntity();
-                will(returnValue(false));
-
-                allowing(mockStringSpec).isValue();
-                will(returnValue(true));
-
-                ignoring(mockStringSpec).assertPojoCompatible(with(any(String.class)));
-
-                allowing(mockSpecificationLoader).specForType(Integer.class);
-                will(returnValue(Optional.of(mockIntegerSpec)));
-
-                allowing(mockIntegerSpec).getCorrespondingClass();
-                will(returnValue(Integer.class));
-
-                allowing(mockIntegerSpec).isValue();
-                will(returnValue(true));
-
-                allowing(mockIntegerSpec).isNonScalar();
-                will(returnValue(false));
-
-                allowing(mockIntegerSpec).isScalar();
-                will(returnValue(true));
-
-                allowing(mockIntegerSpec).isEntity();
-                will(returnValue(false));
-
-                allowing(mockIntegerSpec).getTitle(with(any(TitleRenderRequest.class)));
-                will(returnValue("3"));
-
-                ignoring(mockIntegerSpec).assertPojoCompatible(with(any(Integer.class)));
-                ignoring(mockIntegerSpec).assertPojoCompatible(with(any(int.class)));
-
-            }
-        });
-        final String title = titleFacetViaTitleAnnotation.title(
-                TitleRenderRequest.builder()
-                .object(mockObjectAdapter)
-                .build());
-        assertThat(title, is("titleElement1 titleElement3 titleElement5 3 this needs to be trimmed"));
+        assertThat(objectAdapter.getTitle(),
+                is("titleElement1 titleElement3 titleElement5 3 this needs to be trimmed"));
     }
 
 
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/param/parameter/ParameterAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/param/parameter/ParameterAnnotationFacetFactoryTest.java
index 7a73a874b1..62370ea448 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/param/parameter/ParameterAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/param/parameter/ParameterAnnotationFacetFactoryTest.java
@@ -32,9 +32,9 @@ import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import org.apache.isis.applib.annotation.Introspection.IntrospectionPolicy;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Parameter;
-import org.apache.isis.applib.annotation.Introspection.IntrospectionPolicy;
 import org.apache.isis.applib.spec.Specification;
 import org.apache.isis.core.metamodel.facets.AbstractFacetFactoryJUnit4TestCase;
 import org.apache.isis.core.metamodel.facets.FacetFactory;
@@ -65,16 +65,6 @@ public class ParameterAnnotationFacetFactoryTest extends AbstractFacetFactoryJUn
         }});
     }
 
-    void allowingLoadSpecificationRequestsFor(final Class<?> cls, final Class<?> returnType) {
-        context.checking(new Expectations() {{
-            allowing(mockSpecificationLoader).loadSpecification(cls);
-            will(returnValue(mockTypeSpec));
-
-            allowing(mockSpecificationLoader).loadSpecification(returnType);
-            will(returnValue(mockReturnTypeSpec));
-        }});
-    }
-
     @Before
     public void setUp() throws Exception {
         facetFactory = new ParameterAnnotationFacetFactory(metaModelContext);
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
index c5ce69a0df..f1523c2095 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
@@ -52,7 +52,6 @@ import org.apache.isis.core.metamodel.facets.FacetFactory;
 import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext;
 import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
 import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet;
-import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.PropertyDomainEventDefaultFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.mustsatisfyspec.MustSatisfySpecificationFacet;
@@ -100,20 +99,6 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
         }});
     }
 
-    void allowingLoadSpecificationRequestsFor(final Class<?> cls, final Class<?> returnType) {
-        context.checking(new Expectations() {{
-            allowing(mockSpecificationLoader).loadSpecification(cls);
-            will(returnValue(mockTypeSpec));
-
-            allowing(mockSpecificationLoader).loadSpecification(returnType);
-            will(returnValue(mockReturnTypeSpec));
-
-            allowing(mockTypeSpec).getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class);
-            will(returnValue(null));
-
-        }});
-    }
-
     private static void processModify(
             final PropertyAnnotationFacetFactory facetFactory, final FacetFactory.ProcessMethodContext processMethodContext) {
         val propertyIfAny = processMethodContext.synthesizeOnMethod(Property.class);
@@ -236,14 +221,6 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             addSetterFacet(facetedMethod);
             addClearFacet(facetedMethod);
 
-            // expect
-            allowingLoadSpecificationRequestsFor(cls, propertyMethod.getReturnType());
-            context.checking(new Expectations() {{
-
-                allowing(mockTypeSpec).getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class);
-                will(returnValue(null));
-            }});
-
             // when
             val processMethodContext = ProcessMethodContext
                     .forTesting(cls, null,
@@ -291,14 +268,6 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             addSetterFacet(facetedMethod);
             addClearFacet(facetedMethod);
 
-            // expect
-            allowingLoadSpecificationRequestsFor(cls, propertyMethod.getReturnType());
-
-            context.checking(new Expectations() {{
-                allowing(mockTypeSpec).getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class);
-                will(returnValue(null));
-            }});
-
             // when
             val processMethodContext = ProcessMethodContext
                     .forTesting(cls, null,
@@ -346,13 +315,6 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             addSetterFacet(facetedMethod);
             addClearFacet(facetedMethod);
 
-            // expect
-            allowingLoadSpecificationRequestsFor(cls, propertyMethod.getReturnType());
-
-            context.checking(new Expectations() {{
-                allowing(mockTypeSpec).getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class);
-                will(returnValue(null));
-            }});
             // when
             val processMethodContext = ProcessMethodContext
                     .forTesting(cls, null,
@@ -400,9 +362,6 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             addSetterFacet(facetedMethod);
             addClearFacet(facetedMethod);
 
-            // expect
-            allowingLoadSpecificationRequestsFor(cls, propertyMethod.getReturnType());
-
             // when
             val processMethodContext = ProcessMethodContext
                     .forTesting(cls, null,
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java
new file mode 100644
index 0000000000..a6cd9570bc
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java
@@ -0,0 +1,89 @@
+/*
+ *  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 org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.isis.core.metamodel._testing.MetaModelContext_forTesting;
+import org.apache.isis.core.metamodel.object.ManagedObject.Specialization;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.valuesemantics.IntValueSemantics;
+
+import lombok.val;
+
+class ManagedObjectTest {
+
+    private MetaModelContext_forTesting mmc;
+    private SpecificationLoader specLoader;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        mmc = MetaModelContext_forTesting.builder()
+                        .valueSemantic(new IntValueSemantics())
+                        .build();
+        specLoader = mmc.getSpecificationLoader();
+    }
+
+    @ParameterizedTest
+    @ValueSource(classes = {void.class, Void.class})
+    void voidShouldMapToEmptyValue(final Class<?> cls) {
+        val spec = specLoader.specForTypeElseFail(cls);
+        assertTrue(spec.isVoid(), ()->"isVoid()");
+        assertTrue(spec.isValue(), ()->"isValue()");
+        assertFalse(spec.isAbstract(), ()->"isAbstract()");
+        assertFalse(spec.isInjectable(), ()->"isInjectable()");
+        assertFalse(spec.isEntityOrViewModel(), ()->"isEntityOrViewModel()");
+
+        val emptySpez = Specialization.inferFrom(spec, null);
+        assertEquals(Specialization.EMPTY, emptySpez);
+
+        val emptyObject = ManagedObject.empty(spec);
+        assertNotNull(emptyObject);
+    }
+
+    @ParameterizedTest
+    @ValueSource(classes = {int.class, Integer.class})
+    void intShouldMapToValue(final Class<?> cls) {
+        val spec = specLoader.specForTypeElseFail(cls);
+        assertFalse(spec.isVoid(), ()->"isVoid()");
+        assertTrue(spec.isValue(), ()->"isValue()");
+        assertFalse(spec.isAbstract(), ()->"isAbstract()");
+        assertFalse(spec.isInjectable(), ()->"isInjectable()");
+        assertFalse(spec.isEntityOrViewModel(), ()->"isEntityOrViewModel()");
+
+        val emptySpez = Specialization.inferFrom(spec, null);
+        assertEquals(Specialization.EMPTY, emptySpez);
+
+        val emptyObject = ManagedObject.empty(spec);
+        assertNotNull(emptyObject);
+
+        val presentObject = ManagedObject.wrapScalar(specLoader, 3);
+        assertEquals(Specialization.VALUE, presentObject.getSpecialization());
+
+        presentObject.assertCompliance(6);
+    }
+
+}
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/_ObjectMemento.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/_ObjectMemento.java
index f15aa0204a..4cf1ed7816 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/_ObjectMemento.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/_ObjectMemento.java
@@ -561,7 +561,7 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
         }
 
         // intercept when managed by IoCC
-        if(spec.getBeanSort().isManagedBean()) {
+        if(spec.getBeanSort().isManagedBeanAny()) {
             return spec.getMetaModelContext().lookupServiceAdapterById(getLogicalTypeName());
         }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
index 6704d64579..137290a0cc 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
@@ -421,7 +421,7 @@ implements ActionPromptProvider {
         }
 
         val dialogMode =
-                sort.isManagedBean()
+                sort.isManagedBeanAny()
                         ? getCommonContext().getConfiguration().getViewer().getWicket().getDialogModeForMenu()
                         : getCommonContext().getConfiguration().getViewer().getWicket().getDialogMode();
         switch (dialogMode) {
diff --git a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/choices/ChoiceProviderForValuesTest.java b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/choices/ChoiceProviderForValuesTest.java
index 2dc293f558..96c78216aa 100644
--- a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/choices/ChoiceProviderForValuesTest.java
+++ b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/choices/ChoiceProviderForValuesTest.java
@@ -35,7 +35,8 @@ import org.apache.isis.viewer.wicket.ui.components.widgets.select2.providers.Cho
 
 import lombok.val;
 
-class ChoiceProviderForValuesTest extends ChoiceProviderTestAbstract {
+class ChoiceProviderForValuesTest
+extends ChoiceProviderTestAbstract {
 
     @BeforeEach
     void setup() throws Exception {