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/10/23 12:17:39 UTC

[isis] branch master updated: ISIS-2424: make SpecificationCache an interface an simplify implementation

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 24cb695  ISIS-2424: make SpecificationCache an interface an simplify implementation
24cb695 is described below

commit 24cb6957d92225d4c0a3be915b1890a9d3c270d2
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Oct 23 14:17:22 2020 +0200

    ISIS-2424: make SpecificationCache an interface an simplify
    implementation
---
 .../org/apache/isis/commons/collections/Can.java   |  8 ++-
 .../apache/isis/commons/collections/Can_Empty.java |  5 ++
 .../isis/commons/collections/Can_Multiple.java     |  5 ++
 .../isis/commons/collections/Can_Singleton.java    |  5 ++
 .../ApplicationFeatureRepositoryDefault.java       | 10 ++--
 .../services/layout/LayoutServiceDefault.java      | 14 ++---
 .../metamodel/MetaModelServiceDefault.java         |  7 ++-
 .../metamodel/specloader/SpecificationCache.java   | 57 +++++++++---------
 .../specloader/SpecificationCacheDefault.java      | 70 ++++++++++++----------
 .../metamodel/specloader/SpecificationLoader.java  |  4 +-
 .../specloader/SpecificationLoaderDefault.java     | 25 ++++----
 .../SpecificationLoaderDefault_debug.java          |  4 +-
 .../specloader/SpecificationCacheDefaultTest.java  | 18 +++---
 .../isis/testdomain/domainmodel/MetamodelUtil.java |  4 +-
 .../domaintypes/TypeListReprRenderer.java          | 10 ++--
 .../resources/DomainTypeResourceServerside.java    |  8 +--
 .../viewer/resources/HomePageReprRenderer.java     |  4 +-
 17 files changed, 139 insertions(+), 119 deletions(-)

diff --git a/commons/src/main/java/org/apache/isis/commons/collections/Can.java b/commons/src/main/java/org/apache/isis/commons/collections/Can.java
index dd91e65..b171c3a 100644
--- a/commons/src/main/java/org/apache/isis/commons/collections/Can.java
+++ b/commons/src/main/java/org/apache/isis/commons/collections/Can.java
@@ -101,6 +101,11 @@ public interface Can<T> extends Iterable<T>, Serializable {
      * @return Stream of elements this Can contains
      */
     Stream<T> stream();
+    
+    /**
+     * @return possibly concurrent Stream of elements this Can contains
+     */
+    Stream<T> parallelStream();
 
     /**
      * @return this Can's first element or an empty Optional if no such element
@@ -577,6 +582,5 @@ public interface Can<T> extends Iterable<T>, Serializable {
      */
     T[] toArray(Class<T> elementType);
 
-    
-    
+        
 }
diff --git a/commons/src/main/java/org/apache/isis/commons/collections/Can_Empty.java b/commons/src/main/java/org/apache/isis/commons/collections/Can_Empty.java
index 51f0f4b..7b1f81e 100644
--- a/commons/src/main/java/org/apache/isis/commons/collections/Can_Empty.java
+++ b/commons/src/main/java/org/apache/isis/commons/collections/Can_Empty.java
@@ -54,6 +54,11 @@ final class Can_Empty<T> implements Can<T> {
     public Stream<T> stream() {
         return Stream.empty();
     }
+    
+    @Override
+    public Stream<T> parallelStream() {
+        return Stream.empty();
+    }
 
     @Override
     public Optional<T> getSingleton() {
diff --git a/commons/src/main/java/org/apache/isis/commons/collections/Can_Multiple.java b/commons/src/main/java/org/apache/isis/commons/collections/Can_Multiple.java
index ea1af08..a3e8470 100644
--- a/commons/src/main/java/org/apache/isis/commons/collections/Can_Multiple.java
+++ b/commons/src/main/java/org/apache/isis/commons/collections/Can_Multiple.java
@@ -60,6 +60,11 @@ final class Can_Multiple<T> implements Can<T> {
     public Stream<T> stream() {
         return elements.stream();
     }
+    
+    @Override
+    public Stream<T> parallelStream() {
+        return elements.parallelStream();
+    }
 
     @Override
     public Optional<T> getSingleton() {
diff --git a/commons/src/main/java/org/apache/isis/commons/collections/Can_Singleton.java b/commons/src/main/java/org/apache/isis/commons/collections/Can_Singleton.java
index c9c12c3..449bf9f 100644
--- a/commons/src/main/java/org/apache/isis/commons/collections/Can_Singleton.java
+++ b/commons/src/main/java/org/apache/isis/commons/collections/Can_Singleton.java
@@ -59,6 +59,11 @@ final class Can_Singleton<T> implements Can<T> {
     public Stream<T> stream() {
         return Stream.of(element);
     }
+    
+    @Override
+    public Stream<T> parallelStream() {
+        return Stream.of(element);
+    }
 
     @Override
     public Optional<T> getFirst() {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
index 3e036e6..a5c90f4 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
@@ -37,6 +37,7 @@ import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
 import org.apache.isis.applib.services.appfeat.ApplicationMemberType;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.core.config.IsisConfiguration;
@@ -120,15 +121,16 @@ public class ApplicationFeatureRepositoryDefault implements ApplicationFeatureRe
             return;
         }
         initializationState = InitializationState.INITIALIZED;
-        final Collection<ObjectSpecification> specifications = primeMetaModel();
-        for (val spec : specifications) {
+        val allSpecs = primeMetaModel();
+        for (val spec : allSpecs) {
             createApplicationFeaturesFor(spec);
         }
     }
 
-    private Collection<ObjectSpecification> primeMetaModel() {
+    private Can<ObjectSpecification> primeMetaModel() {
+        log.warn("loading service specs here might be redundant");
         serviceRegistry.streamRegisteredBeans()
-        .forEach(bean->specificationLoader.loadSpecification(bean.getBeanClass()));
+            .forEach(bean->specificationLoader.loadSpecification(bean.getBeanClass()));
         return specificationLoader.snapshotSpecifications();
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
index 5f42ddb..1cb8c0a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
@@ -19,8 +19,6 @@
 package org.apache.isis.core.metamodel.services.layout;
 
 import java.io.File;
-import java.util.Collection;
-import java.util.List;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -39,7 +37,6 @@ import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.services.layout.LayoutService;
 import org.apache.isis.applib.services.menu.MenuBarsService;
 import org.apache.isis.applib.util.ZipWriter;
-import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -98,13 +95,10 @@ public class LayoutServiceDefault implements LayoutService {
 
     @Override
     public byte[] toZip(final Style style) {
-        final Collection<ObjectSpecification> allSpecs = specificationLoader.snapshotSpecifications();
-        final List<ObjectSpecification> domainObjectSpecs = _Lists
-                .filter(allSpecs, spec ->
-                !spec.isAbstract() &&
-                (spec.isEntity() || spec.isViewModel())
-                        );
-
+        val domainObjectSpecs = specificationLoader.snapshotSpecifications()
+        .filter(spec ->
+                !spec.isAbstract() 
+                && (spec.isEntity() || spec.isViewModel()));
 
         val zipWriter = ZipWriter.ofFailureMessage("Unable to create zip of layouts");
 
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 9115655..766b8f9 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
@@ -18,7 +18,6 @@
  */
 package org.apache.isis.core.metamodel.services.metamodel;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.stream.Stream;
@@ -36,11 +35,11 @@ import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
 import org.apache.isis.applib.services.grid.GridService;
+import org.apache.isis.applib.services.metamodel.BeanSort;
 import org.apache.isis.applib.services.metamodel.DomainMember;
 import org.apache.isis.applib.services.metamodel.DomainModel;
 import org.apache.isis.applib.services.metamodel.MetaModelService;
 import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.applib.services.metamodel.BeanSort;
 import org.apache.isis.core.metamodel.facets.actions.command.CommandFacet;
 import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureId;
@@ -56,6 +55,8 @@ import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.schema.metamodel.v2.MetamodelDto;
 
+import lombok.val;
+
 @Service
 @Named("isisMetaModel.MetaModelServiceDefault")
 @Order(OrderPrecedence.MIDPOINT)
@@ -108,7 +109,7 @@ public class MetaModelServiceDefault implements MetaModelService {
     @Override
     public DomainModel getDomainModel() {
 
-        final Collection<ObjectSpecification> specifications = specificationLoader.snapshotSpecifications();
+        val specifications = specificationLoader.snapshotSpecifications();
 
         final List<DomainMember> rows = _Lists.newArrayList();
         for (final ObjectSpecification spec : specifications) {
diff --git a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/domainmodel/MetamodelUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCache.java
similarity index 50%
copy from regressiontests/incubating/src/test/java/org/apache/isis/testdomain/domainmodel/MetamodelUtil.java
copy to core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCache.java
index 1c26a6e..406770e 100644
--- a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/domainmodel/MetamodelUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCache.java
@@ -16,36 +16,35 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.testdomain.domainmodel;
+package org.apache.isis.core.metamodel.specloader;
 
-import java.util.Collection;
-import java.util.Set;
+import java.util.Optional;
+import java.util.function.Function;
 
-import org.apache.isis.commons.internal.collections._Sets;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.collections.snapshot._VersionedList;
+import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.spec.feature.Contributed;
-
-import lombok.val;
-
-final class MetamodelUtil {
-
-    static Set<String> featuresSummarized(Collection<ObjectSpecification> allSpecs){
-        
-        val features = _Sets.<String>newHashSet();
-        
-        allSpecs.stream()
-        .forEach(spec->{
-            spec.streamObjectActions(Contributed.INCLUDED)
-            .forEach(feature->{
-                features.add(feature.getIdentifier().toString());
-            });
-            spec.streamAssociations(Contributed.INCLUDED)
-            .forEach(feature->{
-                features.add(feature.getIdentifier().toString());
-            });
-        });
-        
-        return features;
-    }
+
+interface SpecificationCache<T extends ObjectSpecification> {
+
+    _VersionedList<T> getVList();
     
-}
+    Optional<T> lookup(Class<?> cls);
+
+    T computeIfAbsent(Class<?> cls, Function<Class<?>, T> mappingFunction);
+    
+    T remove(Class<?> cls);
+
+    void clear();
+
+    /** @returns thread-safe defensive copy */
+    Can<T> snapshotSpecs();
+
+    Class<?> resolveType(ObjectSpecId objectSpecID);
+
+    T getByObjectType(ObjectSpecId objectSpecID);
+
+    void recache(T spec);
+
+}
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
index 1612500..441310c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
@@ -19,11 +19,11 @@
 
 package org.apache.isis.core.metamodel.specloader;
 
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Function;
 
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.commons.internal.collections.snapshot._VersionedList;
 import org.apache.isis.core.metamodel.commons.ClassUtil;
@@ -31,97 +31,103 @@ import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFac
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
-import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.val;
 
 
-class SpecificationCacheDefault<T extends ObjectSpecification> {
+class SpecificationCacheDefault<T extends ObjectSpecification> implements SpecificationCache<T> {
 
-    private final Map<ObjectSpecId, String> classNameBySpecId = _Maps.newHashMap();
-    private final Map<String, T> specByClassName = _Maps.newHashMap();
+    private final Map<ObjectSpecId, Class<?>> classBySpecId = _Maps.newHashMap();
+    private final Map<Class<?>, T> specByClass = _Maps.newHashMap();
     
     
     // optimization: specialized list to keep track of any additions to the cache fast
-    @Getter(value = AccessLevel.PACKAGE)
+    @Getter(onMethod_ = {@Override})//(value = AccessLevel.PACKAGE)
     private final _VersionedList<T> vList = new _VersionedList<>(); 
     
-    public T get(String className) {
-        return specByClassName.get(className);
+    @Override
+    public Optional<T> lookup(Class<?> cls) {
+        return Optional.ofNullable(specByClass.get(cls));
     }
     
+    @Override
     public T computeIfAbsent(
-            String className, 
-            Function<? super String, T> mappingFunction) {
+            Class<?> cls, 
+            Function<Class<?>, T> mappingFunction) {
         
-        T spec = specByClassName.get(className);
+        T spec = specByClass.get(cls);
         if(spec==null) {
-            spec = mappingFunction.apply(className);
+            spec = mappingFunction.apply(cls);
             internalPut(spec);
         }
         return spec;
     }
 
+    @Override
     public void clear() {
         synchronized(this) {
-            specByClassName.clear();
-            classNameBySpecId.clear();
+            specByClass.clear();
+            classBySpecId.clear();
             vList.clear();
         }
     }
 
     /** @returns thread-safe defensive copy */
-    public Collection<T> snapshotSpecs() {
+    @Override
+    public Can<T> snapshotSpecs() {
         synchronized(this) {
-            return new ArrayList<T>(specByClassName.values());
+            return Can.ofCollection(specByClass.values());
         }
     }
     
+    @Override
     public Class<?> resolveType(final ObjectSpecId objectSpecID) {
-        val classNameFromCache = classNameBySpecId.get(objectSpecID);
-        val className = classNameFromCache != null
-                ? classNameFromCache 
-                : objectSpecID.asString();
-        return ClassUtil.forNameElseNull(className);
+        val classFromCache = classBySpecId.get(objectSpecID);
+        return classFromCache != null
+                ? classFromCache 
+                : ClassUtil.forNameElseNull(objectSpecID.asString());
     }
     
+    @Override
     public T getByObjectType(final ObjectSpecId objectSpecID) {
-        val className = classNameBySpecId.get(objectSpecID);
-        return className != null ? specByClassName.get(className) : null;
+        val className = classBySpecId.get(objectSpecID);
+        return className != null ? specByClass.get(className) : null;
     }
 
     private void internalPut(T spec) {
         if(spec==null) {
             return;
         }
-        val className = spec.getCorrespondingClass().getName();
+        val cls = spec.getCorrespondingClass();
         val specId = spec.getSpecId();
-        val existing = specByClassName.put(className, spec);
+        val existing = specByClass.put(cls, spec);
         if(existing==null) {
             vList.add(spec); // add to vList only if we don't have it already
         }
         if (specId == null) {
             return;
         }
-        classNameBySpecId.put(specId, className);
+        classBySpecId.put(specId, cls);
     }
 
-    public T remove(String typeName) {
-        final T removed = specByClassName.remove(typeName);
+    @Override
+    public T remove(Class<?> cls) {
+        final T removed = specByClass.remove(cls);
         if(removed!=null) {
             vList.clear(); // invalidate
-            vList.addAll(specByClassName.values());
+            vList.addAll(specByClass.values());
         }
         if(hasUsableSpecId(removed)) {
             val specId = removed.getSpecId();
-            classNameBySpecId.remove(specId);
+            classBySpecId.remove(specId);
         }
         return removed;
     }
 
+    @Override
     public void recache(T spec) {
         if(hasUsableSpecId(spec)) {
-            classNameBySpecId.put(spec.getSpecId(), spec.getCorrespondingClass().getName());
+            classBySpecId.put(spec.getSpecId(), spec.getCorrespondingClass());
         }
     }
     
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
index 862fe84..41fddea 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
@@ -18,11 +18,11 @@
  */
 package org.apache.isis.core.metamodel.specloader;
 
-import java.util.Collection;
 import java.util.function.Consumer;
 
 import javax.annotation.Nullable;
 
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.core.metamodel.services.classsubstitutor.ClassSubstitutor;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
@@ -71,7 +71,7 @@ public interface SpecificationLoader {
      * 
      * @return snapshot of all the (currently) loaded specifications, a defensive-copy 
      */
-    Collection<ObjectSpecification> snapshotSpecifications();
+    Can<ObjectSpecification> snapshotSpecifications();
     
     /**
      * Similar to {@link #snapshotSpecifications()}, but also handles concurrent additions that occur 
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 e5f9644..d1c5595 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
@@ -19,7 +19,6 @@
 package org.apache.isis.core.metamodel.specloader;
 
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -41,6 +40,7 @@ import org.springframework.stereotype.Service;
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.metamodel.BeanSort;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.base._Blackhole;
 import org.apache.isis.commons.internal.base._Lazy;
@@ -121,7 +121,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 
     private FacetProcessor facetProcessor;
 
-    private final SpecificationCacheDefault<ObjectSpecification> cache = new SpecificationCacheDefault<>();
+    private final SpecificationCache<ObjectSpecification> cache = new SpecificationCacheDefault<>();
 
     /**
      * We only ever mark the meta-model as fully introspected if in {@link #isFullIntrospect() full} 
@@ -232,8 +232,9 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 
         val valueTypeSpecs = _Lists.<ObjectSpecification>newArrayList();
 
-        valueTypeRegistry.streamRegisteredClasses().forEach(clazz -> {
-            val spec = loadSpecification(clazz, IntrospectionState.NOT_INTROSPECTED);
+        valueTypeRegistry.streamRegisteredClasses()
+        .forEach(cls -> {
+            val spec = loadSpecification(cls, IntrospectionState.NOT_INTROSPECTED);
             if(spec!=null) {
                 knownSpecs.add(spec);
                 valueTypeSpecs.add(spec);
@@ -268,10 +269,10 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         SpecificationLoaderDefault_debug.logBefore(log, cache, knownSpecs);
 
         log.info(" - introspecting {} type hierarchies", knownSpecs.size());
-        introspect(knownSpecs, IntrospectionState.TYPE_INTROSPECTED);
+        introspect(Can.ofCollection(knownSpecs), IntrospectionState.TYPE_INTROSPECTED);
 
         log.info(" - introspecting {} value types", valueTypeSpecs.size());
-        introspect(valueTypeSpecs, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+        introspect(Can.ofCollection(valueTypeSpecs), IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
 
         log.info(" - introspecting {} managed beans contributing (aka domain services)", isisBeanTypeRegistry.getManagedBeansContributing().size());
         log.info(" - introspecting {} mixins", isisBeanTypeRegistry.getMixinTypes().size());
@@ -279,7 +280,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
                 isisBeanTypeRegistry.getEntityTypesJdo().size(),
                 isisBeanTypeRegistry.getEntityTypesJpa().size());
         log.info(" - introspecting {} view models", isisBeanTypeRegistry.getViewModelTypes().size());
-        introspect(domainObjectSpecs, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+        introspect(Can.ofCollection(domainObjectSpecs), IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
 
         SpecificationLoaderDefault_debug.logAfter(log, cache, knownSpecs);
 
@@ -395,13 +396,11 @@ 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);'
         synchronized (cache) {
-            cachedSpec = cache.computeIfAbsent(typeName, __->
+            cachedSpec = cache.computeIfAbsent(substitutedType, __->
                 createSpecification(substitutedType, beanClassifier.apply(type)));
         }
 
@@ -476,7 +475,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
     // -- LOOKUP
 
     @Override
-    public Collection<ObjectSpecification> snapshotSpecifications() {
+    public Can<ObjectSpecification> snapshotSpecifications() {
         return cache.snapshotSpecs();
     }
     
@@ -563,7 +562,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
     }
 
     private void introspect(
-            final Collection<ObjectSpecification> specs, 
+            final Can<ObjectSpecification> specs, 
             final IntrospectionState upTo) {
 
         val isConcurrentFromConfig = isisConfiguration.getCore().getMetaModel().getIntrospector().isParallelize();
@@ -603,7 +602,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         
         while(spec != null) {
             val type = spec.getCorrespondingClass();
-            cache.remove(type.getName());
+            cache.remove(type);
             spec = spec.superclass();
         }
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault_debug.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault_debug.java
index b21aa04..79f33a1 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault_debug.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault_debug.java
@@ -32,7 +32,7 @@ final class SpecificationLoaderDefault_debug {
 
     static void logBefore(
             Logger log, 
-            SpecificationCacheDefault<ObjectSpecification> cache, 
+            SpecificationCache<ObjectSpecification> cache, 
             List<? extends ObjectSpecification> scanned) {
         
         if(!log.isDebugEnabled()) {
@@ -59,7 +59,7 @@ final class SpecificationLoaderDefault_debug {
 
     static void logAfter(
             Logger log, 
-            SpecificationCacheDefault<ObjectSpecification> cache, 
+            SpecificationCache<ObjectSpecification> cache, 
             Collection<? extends ObjectSpecification> scanned) {
         
         if(!log.isDebugEnabled()) {
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefaultTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefaultTest.java
index e9fca68..016470d 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefaultTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefaultTest.java
@@ -29,6 +29,7 @@ import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 
 import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
 import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2.Mode;
@@ -47,7 +48,7 @@ public class SpecificationCacheDefaultTest {
     @Mock
     private ObjectSpecification orderSpec;
 
-    private SpecificationCacheDefault<ObjectSpecification> specificationCache;
+    private SpecificationCache<ObjectSpecification> specificationCache;
 
     @Before
     public void setUp() throws Exception {
@@ -79,15 +80,16 @@ public class SpecificationCacheDefaultTest {
 
     @Test
     public void get_whenNotCached() {
-        assertNull(specificationCache.get(Customer.class.getName()));
+        assertFalse(specificationCache.lookup(Customer.class).isPresent());
     }
 
     @Test
     public void get_whenCached() {
-        final String customerClassName = Customer.class.getName();
-        specificationCache.computeIfAbsent(customerClassName, __->customerSpec);
+        
+        specificationCache.computeIfAbsent(Customer.class, __->customerSpec);
 
-        final ObjectSpecification objectSpecification = specificationCache.get(customerClassName);
+        final ObjectSpecification objectSpecification = specificationCache.lookup(Customer.class)
+                .orElse(null);
 
         assertSame(objectSpecification, customerSpec);
     }
@@ -95,8 +97,8 @@ public class SpecificationCacheDefaultTest {
 
     @Test
     public void allSpecs_whenCached() {
-        specificationCache.computeIfAbsent(Customer.class.getName(), __->customerSpec);
-        specificationCache.computeIfAbsent(Order.class.getName(), __->orderSpec);
+        specificationCache.computeIfAbsent(Customer.class, __->customerSpec);
+        specificationCache.computeIfAbsent(Order.class, __->orderSpec);
 
         val allSpecs = specificationCache.snapshotSpecs();
 
@@ -111,7 +113,7 @@ public class SpecificationCacheDefaultTest {
     @Test
     public void getByObjectType_whenSet() {
         
-        specificationCache.computeIfAbsent(Customer.class.getName(), __->customerSpec);
+        specificationCache.computeIfAbsent(Customer.class, __->customerSpec);
         val objectSpec = specificationCache.getByObjectType(ObjectSpecId.of("CUS"));
 
         assertSame(objectSpec, customerSpec);
diff --git a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/domainmodel/MetamodelUtil.java b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/domainmodel/MetamodelUtil.java
index 1c26a6e..2938443 100644
--- a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/domainmodel/MetamodelUtil.java
+++ b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/domainmodel/MetamodelUtil.java
@@ -18,9 +18,9 @@
  */
 package org.apache.isis.testdomain.domainmodel;
 
-import java.util.Collection;
 import java.util.Set;
 
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
@@ -29,7 +29,7 @@ import lombok.val;
 
 final class MetamodelUtil {
 
-    static Set<String> featuresSummarized(Collection<ObjectSpecification> allSpecs){
+    static Set<String> featuresSummarized(Can<ObjectSpecification> allSpecs){
         
         val features = _Sets.<String>newHashSet();
         
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domaintypes/TypeListReprRenderer.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domaintypes/TypeListReprRenderer.java
index 5897c3d..62a522f 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domaintypes/TypeListReprRenderer.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domaintypes/TypeListReprRenderer.java
@@ -18,8 +18,7 @@
  */
 package org.apache.isis.viewer.restfulobjects.rendering.domaintypes;
 
-import java.util.Collection;
-
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.Rel;
@@ -29,16 +28,17 @@ import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder;
 import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs;
 import org.apache.isis.viewer.restfulobjects.rendering.ReprRendererAbstract;
 
-public class TypeListReprRenderer extends ReprRendererAbstract<TypeListReprRenderer, Collection<ObjectSpecification>> {
+public class TypeListReprRenderer 
+extends ReprRendererAbstract<TypeListReprRenderer, Can<ObjectSpecification>> {
 
-    private Collection<ObjectSpecification> specifications;
+    private Can<ObjectSpecification> specifications;
 
     public TypeListReprRenderer(final IResourceContext resourceContext, final LinkFollowSpecs linkFollower, final JsonRepresentation representation) {
         super(resourceContext, linkFollower, RepresentationType.TYPE_LIST, representation);
     }
 
     @Override
-    public TypeListReprRenderer with(final Collection<ObjectSpecification> specifications) {
+    public TypeListReprRenderer with(final Can<ObjectSpecification> specifications) {
         this.specifications = specifications;
         return this;
     }
diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainTypeResourceServerside.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainTypeResourceServerside.java
index 20f0ecf..b73443a 100644
--- a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainTypeResourceServerside.java
+++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainTypeResourceServerside.java
@@ -18,8 +18,6 @@
  */
 package org.apache.isis.viewer.restfulobjects.viewer.resources;
 
-import java.util.Collection;
-
 import javax.inject.Inject;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
@@ -38,7 +36,6 @@ import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
@@ -97,9 +94,10 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
         val resourceContext = createResourceContext(
                 RepresentationType.TYPE_LIST, Where.ANYWHERE, RepresentationService.Intent.NOT_APPLICABLE);
 
-        final Collection<ObjectSpecification> allSpecifications = getSpecificationLoader().snapshotSpecifications();
+        val allSpecifications = getSpecificationLoader().snapshotSpecifications();
 
-        final TypeListReprRenderer renderer = new TypeListReprRenderer(resourceContext, null, JsonRepresentation.newMap());
+        final TypeListReprRenderer renderer = 
+                new TypeListReprRenderer(resourceContext, null, JsonRepresentation.newMap());
         renderer.with(allSpecifications).includesSelf();
 
         return Responses.ofOk(renderer, Caching.ONE_DAY).build();
diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/HomePageReprRenderer.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/HomePageReprRenderer.java
index 57bbb32..9a7abef 100644
--- a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/HomePageReprRenderer.java
+++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/HomePageReprRenderer.java
@@ -18,9 +18,9 @@
  */
 package org.apache.isis.viewer.restfulobjects.viewer.resources;
 
-import java.util.Collection;
 import java.util.stream.Stream;
 
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.security.authentication.AuthenticationSession;
@@ -168,7 +168,7 @@ public class HomePageReprRenderer extends ReprRendererAbstract<HomePageReprRende
         getLinks().arrayAdd(link);
     }
 
-    private void addLinkToDomainTypes(final Collection<ObjectSpecification> specifications) {
+    private void addLinkToDomainTypes(final Can<ObjectSpecification> specifications) {
 
         final JsonRepresentation link = 
                 LinkBuilder.newBuilder(