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/01/20 14:44:13 UTC

[isis] branch master updated: ISIS-2158: splits into 2 variants BeanSort.MANAGED_BEAN

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 f3b7c75  ISIS-2158: splits into 2 variants BeanSort.MANAGED_BEAN
f3b7c75 is described below

commit f3b7c75e82e115bcaf0db8f40d2b223c5438f8d4
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Jan 20 15:43:59 2020 +0100

    ISIS-2158: splits into 2 variants BeanSort.MANAGED_BEAN
    
    - to be introspected
    - or not
    
    also allows domain objects to sub-class from java.util.Collection
---
 .../isis/core/commons/internal/ioc/BeanSort.java   | 31 +++++++++++-
 .../core/config/beans/IsisBeanTypeRegistry.java    | 50 ++++++--------------
 .../core/metamodel/IsisModuleCoreMetamodel.java    |  4 ++
 .../load/ObjectLoader_builtinHandlers.java         |  2 +-
 .../services/appfeat/ApplicationFeature.java       |  2 +
 .../appfeat/ApplicationFeatureFactory.java         |  4 +-
 .../classsubstitutor/ClassSubstitutor.java         |  3 --
 .../classsubstitutor/ClassSubstitutorDefault.java  |  4 +-
 .../ClassSubstitutorForCollections.java            |  1 +
 ....java => ClassSubstitutorForDomainObjects.java} | 43 ++++++++---------
 .../metamodel/MetaModelServiceDefault.java         |  6 ++-
 .../specloader/SpecificationLoaderDefault.java     | 55 +++++++++-------------
 .../specimpl/ObjectSpecificationAbstract.java      |  7 ++-
 .../ValueTypeProviderForBuiltin.java}              | 48 ++++++++-----------
 .../isis/viewer/wicket/ui/pages/PageAbstract.java  |  2 +-
 15 files changed, 130 insertions(+), 132 deletions(-)

diff --git a/core/commons/src/main/java/org/apache/isis/core/commons/internal/ioc/BeanSort.java b/core/commons/src/main/java/org/apache/isis/core/commons/internal/ioc/BeanSort.java
index a721ab5..37c271a 100644
--- a/core/commons/src/main/java/org/apache/isis/core/commons/internal/ioc/BeanSort.java
+++ b/core/commons/src/main/java/org/apache/isis/core/commons/internal/ioc/BeanSort.java
@@ -44,10 +44,20 @@ public enum BeanSort {
     /**
      * Injectable object, associated with a lifecycle context 
      * (application-scoped, request-scoped, ...).
+     * <p>
+     * to be introspected: YES
      */
-    MANAGED_BEAN, 
+    MANAGED_BEAN_CONTRIBUTING, 
 
     /**
+     * Injectable object, associated with a lifecycle context 
+     * (application-scoped, request-scoped, ...).
+     * <p>
+     * to be introspected: NO
+     */
+    MANAGED_BEAN_NOT_CONTRIBUTING,
+    
+    /**
      * Object associated with an 'entity' or 'bean' to act as contributer of 
      * domain actions or properties. Might also be stateful similar to VIEW_MODEL.
      */
@@ -64,9 +74,11 @@ public enum BeanSort {
     COLLECTION,
 
     UNKNOWN;
+    
+    // -- SIMPLE PREDICATES
 
     public boolean isManagedBean() {
-        return this == MANAGED_BEAN;
+        return this == MANAGED_BEAN_CONTRIBUTING || this == MANAGED_BEAN_NOT_CONTRIBUTING;
     }
 
     public boolean isMixin() {
@@ -92,5 +104,20 @@ public enum BeanSort {
     public boolean isUnknown() {
         return this == UNKNOWN;
     }
+    
+    // -- HIGER LEVEL PREDICATES
+    
+    public boolean isToBeIntrospected() {
+        
+        if(isUnknown()) {
+            return false;
+        }
+        if(this == MANAGED_BEAN_NOT_CONTRIBUTING) {
+            return false;
+        }
+        
+        return true;
+    }
+    
 
 }
\ No newline at end of file
diff --git a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeRegistry.java b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeRegistry.java
index 6d1c0e8..6d63f0d 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeRegistry.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeRegistry.java
@@ -26,7 +26,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Consumer;
 
 import javax.enterprise.inject.Vetoed;
 
@@ -108,26 +107,6 @@ public final class IsisBeanTypeRegistry implements IsisComponentScanInterceptor,
 
     // -- FILTER
     
-    public void ifToBeInspectedThen(Class<?> type, Consumer<BeanSort> onInspectionRequired) {
-        
-        val beanSort = quickClassify(type);
-        
-        val isManagedObjectToBeInspected = !beanSort.isManagedBean() 
-                && !beanSort.isUnknown();
-        
-        val isManagedBeanToBeInspected = beanSort.isManagedBean() 
-                && (findNearestAnnotation(type, DomainService.class).isPresent()
-                        // || findNearestAnnotation(type, Service.class).isPresent() 
-                        );
-
-        val isToBeInspected = isManagedBeanToBeInspected || isManagedObjectToBeInspected;
-        
-        if(isToBeInspected) {
-            onInspectionRequired.accept(beanSort);
-        }
-    }
-    
-
     @Override
     public void intercept(TypeMetaData typeMeta) {
         
@@ -143,18 +122,18 @@ public final class IsisBeanTypeRegistry implements IsisComponentScanInterceptor,
         } else {
             typeMeta.setBeanNameOverride(extractObjectType(type).orElse(null));
         }
-
-        ifToBeInspectedThen(type, beanSort->{
-
+        
+        val beanSort = quickClassify(type);
+        
+        if(beanSort.isToBeIntrospected()) {
             addIntrospectableType(beanSort, typeMeta);
             
             if(log.isDebugEnabled()) {
-                log.debug("to-be-inspected: {} [{}]",                        
+                log.debug("to-be-introspected: {} [{}]",                        
                                 type,
                                 beanSort.name());
             }
-            
-        });
+        }
         
     }
     
@@ -191,7 +170,8 @@ public final class IsisBeanTypeRegistry implements IsisComponentScanInterceptor,
             introspectableTypes.put(type, sort);
             
             switch (sort) {
-            case MANAGED_BEAN:
+            case MANAGED_BEAN_CONTRIBUTING:
+            case MANAGED_BEAN_NOT_CONTRIBUTING:
                 managedBeanNamesByType.put(type, typeMeta.getEffectiveBeanName());
                 return;
             case MIXIN:
@@ -214,7 +194,7 @@ public final class IsisBeanTypeRegistry implements IsisComponentScanInterceptor,
         }
     }
     
-    private BeanSort quickClassify(Class<?> type) {
+    public BeanSort quickClassify(Class<?> type) {
 
         requires(type, "type");
         
@@ -222,13 +202,9 @@ public final class IsisBeanTypeRegistry implements IsisComponentScanInterceptor,
             return BeanSort.UNKNOWN; // reject
         }
 
-        if(Collection.class.isAssignableFrom(type)) {
-            return BeanSort.COLLECTION;
-        }
-
         val aDomainService = findNearestAnnotation(type, DomainService.class);
         if(aDomainService.isPresent()) {
-            return BeanSort.MANAGED_BEAN;
+            return BeanSort.MANAGED_BEAN_CONTRIBUTING;
         }
 
         //this takes precedence over whatever @DomainObject has to say
@@ -264,7 +240,11 @@ public final class IsisBeanTypeRegistry implements IsisComponentScanInterceptor,
         }
 
         if(findNearestAnnotation(type, Component.class).isPresent()) {
-            return BeanSort.MANAGED_BEAN;
+            return BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING;
+        }
+        
+        if(Collection.class.isAssignableFrom(type)) {
+            return BeanSort.COLLECTION;
         }
 
         if(Serializable.class.isAssignableFrom(type)) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/IsisModuleCoreMetamodel.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/IsisModuleCoreMetamodel.java
index fd1ed36..b2bb85f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/IsisModuleCoreMetamodel.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/IsisModuleCoreMetamodel.java
@@ -31,6 +31,7 @@ import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureFactory
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureRepositoryDefault;
 import org.apache.isis.core.metamodel.services.classsubstitutor.ClassSubstitutorDefault;
 import org.apache.isis.core.metamodel.services.classsubstitutor.ClassSubstitutorForCollections;
+import org.apache.isis.core.metamodel.services.classsubstitutor.ClassSubstitutorForDomainObjects;
 import org.apache.isis.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
 import org.apache.isis.core.metamodel.services.events.MetamodelEventService;
 import org.apache.isis.core.metamodel.services.events.MetamodelEventSupport_Spring;
@@ -49,6 +50,7 @@ import org.apache.isis.core.metamodel.specloader.ProgrammingModelServiceDefault;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoaderDefault;
 import org.apache.isis.core.metamodel.valuetypes.ValueTypeProviderDefault;
 import org.apache.isis.core.metamodel.valuetypes.ValueTypeProviderForCollections;
+import org.apache.isis.core.metamodel.valuetypes.ValueTypeProviderForBuiltin;
 import org.apache.isis.core.metamodel.valuetypes.ValueTypeRegistry;
 import org.apache.isis.core.security.IsisModuleCoreSecurity;
 
@@ -67,9 +69,11 @@ import org.apache.isis.core.security.IsisModuleCoreSecurity;
         ProgrammingModelInitFilterDefault.class,
         ClassSubstitutorDefault.class,
         ClassSubstitutorForCollections.class,
+        ClassSubstitutorForDomainObjects.class,
         ClassSubstitutorRegistry.class,
         ValueTypeProviderDefault.class,
         ValueTypeProviderForCollections.class,
+        ValueTypeProviderForBuiltin.class,
         ValueTypeRegistry.class,
 
         // @Service's
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
index 34931c1..6efc2ad 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
@@ -59,7 +59,7 @@ final class ObjectLoader_builtinHandlers {
                 // eg "NONEXISTENT:123"
                 return true;
             }
-
+            
             // we don't guard against the identifier being null, because, this is ok 
             // for services and values
             return false;
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeature.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeature.java
index 896ee48..4d7d0ed 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeature.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeature.java
@@ -28,6 +28,7 @@ import javax.enterprise.inject.Vetoed;
 import org.apache.isis.applib.IsisModuleApplib;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.annotation.Value;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
 import org.apache.isis.applib.services.appfeat.ApplicationMemberType;
 import org.apache.isis.applib.util.Equality;
@@ -45,6 +46,7 @@ import org.apache.isis.core.commons.internal.collections._Sets;
  *     {@link ApplicationFeatureRepository}, eg {@link ApplicationFeatureRepository#classNamesContainedIn(String, ApplicationMemberType)}.
  * </p>
  */
+@Value
 public class ApplicationFeature implements Comparable<ApplicationFeature> {
 
     public static abstract class PropertyDomainEvent<T> 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureFactory.java
index 2e9b8a1..c84b96b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureFactory.java
@@ -36,10 +36,10 @@ import org.apache.isis.applib.services.factory.FactoryService;
 @Qualifier("Default")
 public class ApplicationFeatureFactory {
 
+    @Inject FactoryService factoryService;
+    
     public ApplicationFeature newApplicationFeature() {
         return factoryService.instantiate(ApplicationFeature.class);
     }
 
-    @Inject FactoryService factoryService;
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutor.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutor.java
index 00c0808..5aa54f4 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutor.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutor.java
@@ -71,9 +71,6 @@ public interface ClassSubstitutor {
         
         /**
          * Forces the (operand-) class never to be replaced.
-         * 
-         * @implNote not required for ClassSubstitutor implementations!
-         * (for framework internal use when aggregating)
          */
         public static Substitution neverReplaceClass() {
             return NEVER_REPLACE_CLASS;
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorDefault.java
index b79d7b3..acc2761 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorDefault.java
@@ -25,6 +25,7 @@ import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.graph.tree.TreeAdapter;
 
 @Component
 @Named("isisMetaModel.ClassSubstitutorDefault")
@@ -39,7 +40,8 @@ public class ClassSubstitutorDefault extends ClassSubstitutorAbstract {
         ignoreSpringFramework();
         ignoreJacksonAndGson();
         skipDataNucleusProxy();
-
+        
+        ignoreClass(TreeAdapter.class.getName());
     }
 
     protected void ignoreCglib() {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java
index 1fe6adf..ee474fa 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java
@@ -56,6 +56,7 @@ public class ClassSubstitutorForCollections implements ClassSubstitutor {
         if(Collection.class.isAssignableFrom(cls)) {
             return Substitution.replaceWith(Collection.class);
         }
+        
         return Substitution.passThrough(); // indifferent
     }
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java
similarity index 59%
copy from core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java
copy to core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java
index 1fe6adf..f968949 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java
@@ -19,43 +19,40 @@
 
 package org.apache.isis.core.metamodel.services.classsubstitutor;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.Vector;
-
+import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.core.config.beans.IsisBeanTypeRegistry;
+import org.apache.isis.core.config.beans.IsisBeanTypeRegistryHolder;
 
 import lombok.NonNull;
+import lombok.val;
 
 @Component
-@Named("isisMetaModel.ClassSubstitutorForCollections")
-@Order(OrderPrecedence.MIDPOINT - 10)
-public class ClassSubstitutorForCollections implements ClassSubstitutor {
+@Named("isisMetaModel.ClassSubstitutorForDomainObjects")
+@Order(OrderPrecedence.MIDPOINT - 20) // before ClassSubstitutorForCollections
+public class ClassSubstitutorForDomainObjects implements ClassSubstitutor {
+
+    private IsisBeanTypeRegistry isisBeanTypeRegistry;
 
+    @Inject
+    public ClassSubstitutorForDomainObjects(IsisBeanTypeRegistryHolder beanTypeRegistryHolder) {
+        this.isisBeanTypeRegistry = beanTypeRegistryHolder.getIsisBeanTypeRegistry();
+    }
+    
     @Override
     public Substitution getSubstitution(@NonNull Class<?> cls) {
-        if(Vector.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(Vector.class);
-        }
-        if(List.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(List.class);
-        }
-        if(SortedSet.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(SortedSet.class);
-        }
-        if(Set.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(Set.class);
-        }
-        if(Collection.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(Collection.class);
+        
+        val beanSort = isisBeanTypeRegistry.quickClassify(cls);
+        
+        if(beanSort.isToBeIntrospected() && !beanSort.isCollection()) {
+            return Substitution.neverReplaceClass();
         }
         return Substitution.passThrough(); // indifferent
     }
+    
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
index bb39922..92a9283 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
@@ -42,6 +42,7 @@ import org.apache.isis.applib.services.metamodel.MetaModelService;
 import org.apache.isis.core.commons.internal.collections._Lists;
 import org.apache.isis.core.commons.internal.ioc.BeanSort;
 import org.apache.isis.core.metamodel.facets.actions.command.CommandFacet;
+import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacet;
 import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureId;
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureType;
@@ -186,7 +187,10 @@ public class MetaModelServiceDefault implements MetaModelService {
         }
         final ObjectSpecification objectSpec = specificationLoader.loadSpecification(domainType);
         if(objectSpec.isManagedBean()) {
-            return BeanSort.MANAGED_BEAN;
+            if(objectSpec.getFacet(DomainServiceFacet.class)!=null) {
+                return BeanSort.MANAGED_BEAN_CONTRIBUTING;    
+            }
+            return BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING;
         }
         if(objectSpec.isViewModel()) {
             return BeanSort.VIEW_MODEL;
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 843fb3d..e05d88c 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
@@ -166,7 +166,11 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
                 programmingModel, isisConfiguration, isisSystemEnvironment,
                 serviceRegistry, isisBeanTypeRegistryHolder,
                 new ValueTypeRegistry(Collections.singletonList(new ValueTypeProviderDefault())),
-                new ClassSubstitutorRegistry(Collections.unmodifiableList(Arrays.asList(new ClassSubstitutorDefault(), new ClassSubstitutorForCollections()))));
+                new ClassSubstitutorRegistry(_Lists.of(
+                        //new ClassSubstitutorForDomainObjects(),
+                        new ClassSubstitutorForCollections(),
+                        new ClassSubstitutorDefault() 
+                        )));
 
         instance.metaModelContext = serviceRegistry.lookupServiceElseFail(MetaModelContext.class);
         instance.facetProcessor = new FacetProcessor(programmingModel, instance.metaModelContext);
@@ -234,22 +238,8 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
                 typeRegistry.veto(type);
             }
 
-            switch (sort) {
-            case MANAGED_BEAN:
-                domainServiceSpecs.add(spec);
-                return;
-            case MIXIN:
-            case ENTITY:
-            case VIEW_MODEL:
-                domainObjectSpecs.add(spec);
-                return;
-
-            case VALUE:
-            case COLLECTION:
-                // handled by ValueTypeRegistry earlier
-            case UNKNOWN:
-            default:
-                // ignore
+            if(sort.isToBeIntrospected()) {
+                domainObjectSpecs.add(spec);   
             }
 
         });
@@ -380,7 +370,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         val substitutedType = substitute.apply(type);
         
         val typeName = substitutedType.getName();
-
+        
         final ObjectSpecification cachedSpec;
         
         // we try not to block on long running code ... 'spec.introspectUpTo(upTo);'
@@ -455,29 +445,26 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 
         if(isMetamodelFullyIntrospected() 
                 && isisConfiguration.getCore().getMetaModel().getIntrospector().isLockAfterFullIntrospection()) {
-            
+
             val typeRegistry = isisBeanTypeRegistryHolder.getIsisBeanTypeRegistry();
-            
+            val sort = typeRegistry.quickClassify(cls);
+
+//          ISIS-2256:
+//            throw _Exceptions.illegalState(
+//                    "Cannot introspect class '%s' of sort %s, because the metamodel has been fully introspected and is now locked. " +
+//                    "One reason this can happen is if you are attempting to invoke an action through the WrapperFactory " +
+//                    "on a service class incorrectly annotated with Spring's @Service annotation instead of " +
+//                    "@DomainService.",
+//                    cls.getName(), sort);
+
             log.warn("Missed class '{}' when the metamodel was fully introspected.", cls.getName());
-            
-            typeRegistry.ifToBeInspectedThen(cls, sort->{
-            
+            if(sort.isToBeIntrospected()) {
                 log.error("Introspecting class '%s' of sort %s, after the metamodel had been fully introspected and is now locked. " +
                       "One reason this can happen is if you are attempting to invoke an action through the WrapperFactory " +
                       "on a service class incorrectly annotated with Spring's @Service annotation instead of " +
                       "@DomainService.",
                         cls.getName(), sort);
-
-//                ISIS-2256: 'fail early' applies only during bootstrapping
-//                never do this when the application is already fully initialized and running    
-//                
-//                throw _Exceptions.illegalState(
-//                        "Cannot introspect class '%s' of sort %s, because the metamodel has been fully introspected and is now locked. " +
-//                        "One reason this can happen is if you are attempting to invoke an action through the WrapperFactory " +
-//                        "on a service class incorrectly annotated with Spring's @Service annotation instead of " +
-//                        "@DomainService.",
-//                        cls.getName(), sort);
-            });
+            }
         }
 
         // ... and create the specs
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 afdce5f..c8a549c 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
@@ -59,6 +59,7 @@ import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
 import org.apache.isis.core.metamodel.facets.all.named.NamedFacet;
 import org.apache.isis.core.metamodel.facets.collections.CollectionFacet;
 import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet;
+import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacet;
 import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
 import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.facets.object.icon.IconFacet;
@@ -1089,10 +1090,14 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
                 .getIsisBeanTypeRegistry();
     }
 
+    //TODO just make 'sort' a field of ObjectSpecification 
     protected BeanSort sortOf(ObjectSpecification spec) {
 
         if(isManagedBean()) { // <-- not a facet, because we get this information earlier (during class scanning)
-            return BeanSort.MANAGED_BEAN;
+            if(spec.getFacet(DomainServiceFacet.class)!=null) {
+                return BeanSort.MANAGED_BEAN_CONTRIBUTING;    
+            }
+            return BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING;
         }
         if(containsFacet(ValueFacet.class)) {
             return BeanSort.VALUE;
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueTypeProviderForBuiltin.java
similarity index 50%
copy from core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java
copy to core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueTypeProviderForBuiltin.java
index 1fe6adf..8d346c0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueTypeProviderForBuiltin.java
@@ -16,14 +16,9 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
-package org.apache.isis.core.metamodel.services.classsubstitutor;
+package org.apache.isis.core.metamodel.valuetypes;
 
 import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.Vector;
 
 import javax.inject.Named;
 
@@ -31,31 +26,28 @@ import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
-
-import lombok.NonNull;
+import org.apache.isis.applib.graph.SimpleEdge;
+import org.apache.isis.applib.graph.tree.LazyTreeNode;
+import org.apache.isis.core.commons.internal.collections._Lists;
+import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeature;
+import org.apache.isis.schema.common.v2.ValueType;
 
 @Component
-@Named("isisMetaModel.ClassSubstitutorForCollections")
-@Order(OrderPrecedence.MIDPOINT - 10)
-public class ClassSubstitutorForCollections implements ClassSubstitutor {
+@Named("isisMetaModel.ValueTypeProviderForBuiltin")
+@Order(OrderPrecedence.MIDPOINT)
+public class ValueTypeProviderForBuiltin implements ValueTypeProvider {
 
     @Override
-    public Substitution getSubstitution(@NonNull Class<?> cls) {
-        if(Vector.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(Vector.class);
-        }
-        if(List.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(List.class);
-        }
-        if(SortedSet.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(SortedSet.class);
-        }
-        if(Set.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(Set.class);
-        }
-        if(Collection.class.isAssignableFrom(cls)) {
-            return Substitution.replaceWith(Collection.class);
-        }
-        return Substitution.passThrough(); // indifferent
+    public Collection<ValueTypeDefinition> definitions() {
+        return _Lists.of(
+                    
+                    // these are not yet part of the schema (do not map onto any value-types there)
+                    ValueTypeDefinition.of(SimpleEdge.class, ValueType.STRING),
+                    ValueTypeDefinition.of(LazyTreeNode.class, ValueType.STRING),
+                    
+                    ValueTypeDefinition.of(ApplicationFeature.class, ValueType.STRING)
+                    
+                );
     }
+
 }
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 5347665..9028b35 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
@@ -449,7 +449,7 @@ public abstract class PageAbstract extends WebPageBase implements ActionPromptPr
         case INLINE_AS_IF_EDIT:
         default:
             final DialogMode dialogMode =
-                    sort == BeanSort.MANAGED_BEAN
+                    sort.isManagedBean()
                             ? getCommonContext().getConfiguration().getViewer().getWicket().getDialogModeForMenu()
                             : getCommonContext().getConfiguration().getViewer().getWicket().getDialogMode();
             switch (dialogMode) {