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 2021/03/02 13:44:56 UTC

[isis] branch master updated: ISIS-2553: Obj. Spec. Loader improvements (1)

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 824621d  ISIS-2553: Obj. Spec. Loader improvements (1)
824621d is described below

commit 824621db2dd86fd617a5ca4d60e395757fc273e0
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Mar 2 14:44:25 2021 +0100

    ISIS-2553: Obj. Spec. Loader improvements (1)
---
 .../isis/applib/services/bookmark/Bookmark.java    |   4 +-
 .../metamodel/MetaModelServiceDefault.java         |  18 ++--
 .../metamodel/specloader/SpecificationLoader.java  | 111 +++++++++++++++------
 .../common/model/mementos/ActionMemento.java       |   6 +-
 .../resources/DomainObjectResourceServerside.java  |   9 +-
 .../resources/DomainTypeResourceServerside.java    |  35 ++++---
 .../wicket/model/mementos/CollectionMemento.java   |   5 +-
 .../wicket/model/mementos/PropertyMemento.java     |   4 +-
 .../model/models/BookmarkTreeNodeComparator.java   |   2 +-
 .../wicket/model/models/ManagedObjectModel.java    |   4 +-
 .../bookmarkedpages/BookmarkedPagesPanel.java      |   3 +-
 .../ObjectAdapterMementoProviderAbstract.java      |   3 +-
 ...tAdapterMementoProviderForValueChoicesTest.java |   5 +-
 .../integration/ConverterForObjectAdapter.java     |   5 +-
 .../viewer/services/mementos/ObjectMementoWkt.java |  20 ++--
 15 files changed, 150 insertions(+), 84 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java
index f26a757..f94a00d 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java
@@ -113,10 +113,10 @@ public class Bookmark implements Serializable {
         return logicalTypeName + SEPARATOR + id;
     }
     
-    // -- DEPRECATIONS 
+    // -- ALIAS
     
     /**
-     * @deprecated use {@link #getLogicalTypeName()} instead
+     * Alias for {@link #getLogicalTypeName()}.
      */
     public String getObjectType() {
         return getLogicalTypeName();
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 0648106..df26aa9 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
@@ -21,6 +21,7 @@ package org.apache.isis.core.metamodel.services.metamodel;
 import java.util.Collections;
 import java.util.List;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Named;
 
@@ -65,19 +66,16 @@ public class MetaModelServiceDefault implements MetaModelService {
     @Inject private SpecificationLoader specificationLoader;
     @Inject private GridService gridService;
 
+    @Nullable
     @Override
-    public Class<?> fromObjectType(final String objectType) {
-        if(objectType == null) {
-            return null;
-        }
-        final ObjectSpecification objectSpecification = specificationLoader.lookupBySpecIdElseLoad(objectType);
-        return objectSpecification != null
-                ? objectSpecification.getCorrespondingClass()
-                : null;
+    public Class<?> fromObjectType(final @Nullable String objectType) {
+        return specificationLoader.specForLogicalTypeName(objectType)
+                .map(ObjectSpecification::getCorrespondingClass)
+                .orElse(null);
     }
 
     @Override
-    public String toObjectType(final Class<?> domainType) {
+    public String toObjectType(final @Nullable Class<?> domainType) {
         if(domainType == null) {
             return null;
         }
@@ -221,7 +219,7 @@ public class MetaModelServiceDefault implements MetaModelService {
             return null;
         }
 
-        final ObjectSpecification spec = specificationLoader.lookupBySpecIdElseLoad(logicalTypeName);
+        final ObjectSpecification spec = specificationLoader.specForLogicalTypeName(logicalTypeName).orElse(null);
         if(spec == null) {
             return null;
         }
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 5bdab4f..660fde0 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,6 +18,7 @@
  */
 package org.apache.isis.core.metamodel.specloader;
 
+import java.util.Optional;
 import java.util.function.Consumer;
 
 import javax.annotation.Nullable;
@@ -25,6 +26,8 @@ import javax.annotation.Nullable;
 import org.apache.isis.applib.id.LogicalType;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.core.metamodel.services.classsubstitutor.ClassSubstitutor;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -32,6 +35,8 @@ import org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState;
 import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
 
+import static org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED;
+
 import lombok.NonNull;
 import lombok.val;
 
@@ -111,59 +116,109 @@ public interface SpecificationLoader {
      */
     void validateLater(ObjectSpecification objectSpec);
 
-    // -- SHORTCUTS
+    // -- SUPPORT FOR LOOKUP BY LOGICAL TYPE NAME
 
     @Nullable
     default ObjectSpecification loadSpecification(
+            final @Nullable String logicalTypeName, 
+            final @NonNull  IntrospectionState introspectionState) {
+
+        if(_Strings.isNullOrEmpty(logicalTypeName)) {
+            return null;
+        }
+        val logicalType = lookupLogicalType(logicalTypeName);
+        if(logicalType==null) {
+            return null;
+        }
+        return loadSpecification(logicalType.getCorrespondingClass(), introspectionState);
+    }
+    
+    // -- SHORTCUTS - 1
+
+    default Optional<ObjectSpecification> specForLogicalTypeName(
+            final @Nullable String logicalTypeName) {
+        return Optional.ofNullable(
+                loadSpecification(logicalTypeName, TYPE_AND_MEMBERS_INTROSPECTED));
+    }
+    
+    default Optional<ObjectSpecification> specForLogicalType(
+            final @Nullable LogicalType logicalType) {
+        return Optional.ofNullable(logicalType)
+                .map(LogicalType::getCorrespondingClass)
+                .flatMap(this::specForType);
+    }
+    
+    default Optional<ObjectSpecification> specForType(
             final @Nullable Class<?> domainType) {
-        return loadSpecification(domainType, IntrospectionState.TYPE_INTROSPECTED);
+        return Optional.ofNullable(
+                loadSpecification(domainType, TYPE_AND_MEMBERS_INTROSPECTED));
     }
 
-    @Nullable
-    default ObjectSpecification loadSpecification(
+    default Optional<ObjectSpecification> specForBookmark(
+            final @Nullable Bookmark bookmark) {
+        return Optional.ofNullable(bookmark)
+                .map(Bookmark::getLogicalTypeName)
+                .flatMap(this::specForLogicalTypeName);
+    }
+    
+    // -- SHORTCUTS - 2
+
+    default ObjectSpecification specForLogicalTypeNameElseFail(
             final @Nullable String logicalTypeName) {
-        return loadSpecification(logicalTypeName, IntrospectionState.TYPE_INTROSPECTED);
+        return specForLogicalTypeName(logicalTypeName)
+                .orElseThrow(()->_Exceptions.noSuchElement(
+                        "meta-model is not aware of an object-type named '%s'",
+                        _Strings.nullToEmpty(logicalTypeName)));
     }
     
-    @Nullable
-    default ObjectSpecification loadSpecification(
+    default ObjectSpecification specForLogicalTypeElseFail(
             final @Nullable LogicalType logicalType) {
-        return loadSpecification(logicalType.getCorrespondingClass(), IntrospectionState.TYPE_INTROSPECTED);
+        return specForLogicalType(logicalType)
+                .orElseThrow(()->_Exceptions.noSuchElement(
+                        "meta-model is not aware of an object-type '%s'",
+                        logicalType));
     }
     
-    @Nullable
-    default ObjectSpecification loadSpecification(
+    default ObjectSpecification specForTypeElseFail(
+            final @Nullable Class<?> domainType) {
+        return specForType(domainType)
+                .orElseThrow(()->_Exceptions.noSuchElement(
+                        "meta-model is not aware of a type '%s'",
+                        domainType));
+    }
+
+    default ObjectSpecification specForBookmarkElseFail(
             final @Nullable Bookmark bookmark) {
-        return loadSpecification(bookmark.getLogicalTypeName(), IntrospectionState.TYPE_INTROSPECTED);
+        return specForBookmark(bookmark)
+                .orElseThrow(()->_Exceptions.noSuchElement(
+                        "meta-model is not aware of a bookmark's (%s) object-type",
+                        bookmark));
     }
     
+    // -- CAUTION! (use only during meta-model initialization)
+    
     @Nullable
     default ObjectSpecification loadSpecification(
-            final @Nullable String logicalTypeName, 
-            final @NonNull  IntrospectionState introspectionState) {
-
-        if(logicalTypeName==null) {
-            return null;
-        }
-        val logicalType = lookupLogicalType(logicalTypeName);
-        return loadSpecification(logicalType.getCorrespondingClass(), introspectionState);
+            final @Nullable Class<?> domainType) {
+        return loadSpecification(domainType, IntrospectionState.TYPE_INTROSPECTED);
     }
 
-    /**
-     * Lookup a specification that has bean loaded before.
-     * @param objectSpecId
-     * //TODO[2533] rename
-     */
     @Nullable
-    default ObjectSpecification lookupBySpecIdElseLoad(
+    default ObjectSpecification loadSpecification(
             final @Nullable String logicalTypeName) {
-        return loadSpecification(logicalTypeName, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+        return loadSpecification(logicalTypeName, IntrospectionState.TYPE_INTROSPECTED);
     }
     
     @Nullable
-    default ObjectSpecification lookupBySpecIdElseLoad(
+    default ObjectSpecification loadSpecification(
             final @Nullable LogicalType logicalType) {
-        return loadSpecification(logicalType.getCorrespondingClass(), IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+        return loadSpecification(logicalType.getCorrespondingClass(), IntrospectionState.TYPE_INTROSPECTED);
+    }
+    
+    @Nullable
+    default ObjectSpecification loadSpecification(
+            final @Nullable Bookmark bookmark) {
+        return loadSpecification(bookmark.getLogicalTypeName(), IntrospectionState.TYPE_INTROSPECTED);
     }
     
 }
diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/mementos/ActionMemento.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/mementos/ActionMemento.java
index fe15f69..44ed6fd 100644
--- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/mementos/ActionMemento.java
+++ b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/mementos/ActionMemento.java
@@ -28,7 +28,6 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 
 import lombok.Getter;
-import lombok.val;
 
 /**
  * {@link Serializable} representation of a {@link ObjectAction}
@@ -90,8 +89,9 @@ public class ActionMemento implements Serializable {
             String nameParmsId,
             SpecificationLoader specificationLoader) {
         
-        val objectSpec = specificationLoader.lookupBySpecIdElseLoad(owningType);
-        return objectSpec.getActionElseFail(nameParmsId, actionType);
+        return specificationLoader
+                .specForLogicalTypeElseFail(owningType)
+                .getActionElseFail(nameParmsId, actionType);
     }
 
 }
diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
index be54907..ae8ef89 100644
--- a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
+++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
@@ -113,7 +113,9 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
             throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Body is not a map; got %s", objectRepr);
         }
 
-        final ObjectSpecification domainTypeSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
+        val domainTypeSpec = getSpecificationLoader().specForLogicalTypeName(domainType)
+                .orElse(null);        
+        
         if (domainTypeSpec == null) {
             throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Could not determine type of domain object to persist (no class with domainType Id of '%s')", domainType);
         }
@@ -342,8 +344,9 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
             final String domainType,
             final String instanceId) {
         
-        val objectSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
-        val gridFacet = objectSpec.getFacet(GridFacet.class);
+        val gridFacet = getSpecificationLoader().specForLogicalTypeName(domainType)
+        .map(spec->spec.getFacet(GridFacet.class))
+        .orElse(null);
 
         if(gridFacet == null) {
             return Optional.empty();
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 cc7ebdc..38e0834 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
@@ -112,9 +112,9 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
         val resourceContext = createResourceContext(
                 RepresentationType.DOMAIN_TYPE, Where.ANYWHERE, RepresentationService.Intent.NOT_APPLICABLE);
 
-        val objectSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
+        val objectSpec = getSpecificationLoader().specForLogicalTypeName(domainType).orElse(null);
 
-        final DomainTypeReprRenderer renderer = new DomainTypeReprRenderer(resourceContext, null, JsonRepresentation.newMap());
+        val renderer = new DomainTypeReprRenderer(resourceContext, null, JsonRepresentation.newMap());
         renderer.with(objectSpec).includesSelf();
 
         return Responses.ofOk(renderer, Caching.ONE_DAY).build();
@@ -134,8 +134,9 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
         
         val serializationStrategy = resourceContext.getSerializationStrategy();
 
-        val objectSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
-        val gridFacet = objectSpec.getFacet(GridFacet.class);
+        val gridFacet = getSpecificationLoader().specForLogicalTypeName(domainType)
+                .map(spec->spec.getFacet(GridFacet.class))
+                .orElse(null);
         
         final Response.ResponseBuilder builder;
         if(gridFacet == null) {
@@ -159,7 +160,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
         val resourceContext = createResourceContext(
                 RepresentationType.PROPERTY_DESCRIPTION, Where.ANYWHERE, RepresentationService.Intent.NOT_APPLICABLE);
 
-        val parentSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
+        val parentSpec = getSpecificationLoader().specForLogicalTypeName(domainType).orElse(null);
         if (parentSpec == null) {
             throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND);
         }
@@ -187,7 +188,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
         val resourceContext = createResourceContext(
                 RepresentationType.COLLECTION_DESCRIPTION, Where.ANYWHERE, RepresentationService.Intent.NOT_APPLICABLE);
 
-        val parentSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
+        val parentSpec = getSpecificationLoader().specForLogicalTypeName(domainType).orElse(null);
         if (parentSpec == null) {
             throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND);
         }
@@ -215,7 +216,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
         val resourceContext = createResourceContext(
                 RepresentationType.ACTION_DESCRIPTION, Where.ANYWHERE, RepresentationService.Intent.NOT_APPLICABLE);
 
-        val parentSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
+        val parentSpec = getSpecificationLoader().specForLogicalTypeName(domainType).orElse(null);
         if (parentSpec == null) {
             throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND);
         }
@@ -238,7 +239,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
         val resourceContext = createResourceContext(
                 RepresentationType.ACTION_PARAMETER_DESCRIPTION, Where.ANYWHERE, RepresentationService.Intent.NOT_APPLICABLE);
 
-        val parentSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
+        val parentSpec = getSpecificationLoader().specForLogicalTypeName(domainType).orElse(null);
         if (parentSpec == null) {
             throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND);
         }
@@ -273,9 +274,13 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
 
         final String supertype = domainTypeFor(superTypeStr, argsUrlEncoded, "supertype");
 
-        val domainTypeSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
-        val supertypeSpec = getSpecificationLoader().lookupBySpecIdElseLoad(supertype);
-
+        val domainTypeSpec = getSpecificationLoader().specForLogicalTypeName(domainType).orElse(null);
+        val supertypeSpec = getSpecificationLoader().specForLogicalTypeName(supertype).orElse(null);
+        if (domainTypeSpec == null
+                || supertypeSpec == null) {
+            throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND);
+        }
+        
         final TypeActionResultReprRenderer renderer = new TypeActionResultReprRenderer(resourceContext, null, JsonRepresentation.newMap());
 
         final String url = "domain-types/" + domainType + "/type-actions/isSubtypeOf/invoke";
@@ -305,8 +310,12 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
 
         final String subtype = domainTypeFor(subTypeStr, argsUrlEncoded, "subtype");
 
-        val domainTypeSpec = getSpecificationLoader().lookupBySpecIdElseLoad(domainType);
-        val subtypeSpec = getSpecificationLoader().lookupBySpecIdElseLoad(subtype);
+        val domainTypeSpec = getSpecificationLoader().specForLogicalTypeName(domainType).orElse(null);
+        val subtypeSpec = getSpecificationLoader().specForLogicalTypeName(subtype).orElse(null);
+        if (domainTypeSpec == null
+                || subtypeSpec == null) {
+            throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND);
+        }
 
         final TypeActionResultReprRenderer renderer = new TypeActionResultReprRenderer(resourceContext, null, JsonRepresentation.newMap());
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/CollectionMemento.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/CollectionMemento.java
index 8013174..ff9aec6 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/CollectionMemento.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/CollectionMemento.java
@@ -38,10 +38,9 @@ public class CollectionMemento implements Serializable {
     private static final long serialVersionUID = 1L;
 
     private static ObjectSpecification owningSpecFor(OneToManyAssociation association) {
-        
         val specificationLoader = association.getMetaModelContext().getSpecificationLoader();
         val logicalType = association.getIdentifier().getLogicalTypeName();
-        return specificationLoader.lookupBySpecIdElseLoad(logicalType);
+        return specificationLoader.specForLogicalTypeNameElseFail(logicalType);
     }
 
     @Getter private final LogicalType owningType;
@@ -100,7 +99,7 @@ public class CollectionMemento implements Serializable {
             LogicalType owningType,
             String id,
             final SpecificationLoader specificationLoader) {
-        return (OneToManyAssociation) specificationLoader.lookupBySpecIdElseLoad(owningType)
+        return (OneToManyAssociation) specificationLoader.specForLogicalTypeElseFail(owningType)
                 .getAssociationElseFail(id);
     }
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/PropertyMemento.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/PropertyMemento.java
index 2f1b866..811df8a 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/PropertyMemento.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/PropertyMemento.java
@@ -35,7 +35,7 @@ public class PropertyMemento implements Serializable {
 
     private static ObjectSpecification owningSpecFor(OneToOneAssociation property) {
         val specificationLoader = property.getMetaModelContext().getSpecificationLoader();
-        return specificationLoader.lookupBySpecIdElseLoad(property.getIdentifier().getLogicalType());
+        return specificationLoader.specForLogicalTypeElseFail(property.getIdentifier().getLogicalType());
     }
 
     private final LogicalType owningType;
@@ -80,7 +80,7 @@ public class PropertyMemento implements Serializable {
             String identifier,
             final SpecificationLoader specificationLoader) {
         
-        return (OneToOneAssociation) specificationLoader.lookupBySpecIdElseLoad(owningType)
+        return (OneToOneAssociation) specificationLoader.specForLogicalTypeElseFail(owningType)
                 .getAssociationElseFail(identifier);
     }
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BookmarkTreeNodeComparator.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BookmarkTreeNodeComparator.java
index f7a2f90..daa1d7a 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BookmarkTreeNodeComparator.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BookmarkTreeNodeComparator.java
@@ -62,7 +62,7 @@ final class BookmarkTreeNodeComparator implements Comparator<BookmarkTreeNode> {
     }
 
     private String classNameOf(RootOid oid) {
-        return specificationLoader.lookupBySpecIdElseLoad(oid.getLogicalTypeName())
+        return specificationLoader.specForLogicalTypeNameElseFail(oid.getLogicalTypeName())
                 .getIdentifier().getClassName();
     }
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java
index 0ead34e..897ca57 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java
@@ -145,8 +145,8 @@ extends ModelAbstract<ManagedObject> {
     @Synchronized
     public ObjectSpecification getTypeOfSpecification() {
         if(!isObjectSpecMemoized) {
-            val specId = getLogicalElementType().orElse(null);
-            objectSpec = super.getSpecificationLoader().lookupBySpecIdElseLoad(specId);
+            val logicalType = getLogicalElementType().orElse(null);
+            objectSpec = super.getSpecificationLoader().specForLogicalType(logicalType).orElse(null);
             isObjectSpecMemoized = true;
         }
         return objectSpec;
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
index ca9d985..eac11da 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
@@ -163,7 +163,8 @@ public class BookmarkedPagesPanel extends PanelAbstract<BookmarkedPagesModel> {
                     ObjectSpecification objectSpec = null;
                     RootOid oid = node.getOidNoVer();
                     if(oid != null) {
-                        objectSpec = getSpecificationLoader().lookupBySpecIdElseLoad(oid.getLogicalTypeName());
+                        objectSpec = getSpecificationLoader().specForLogicalTypeName(oid.getLogicalTypeName())
+                                .orElse(null);
                     }
                     final ResourceReference imageResource = getImageResourceCache().resourceReferenceForSpec(objectSpec);
                     final Image image = new Image(ID_BOOKMARKED_PAGE_ICON, imageResource) {
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java
index 29909eb..a01dff8 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java
@@ -89,7 +89,8 @@ extends ChoiceProvider<ObjectMemento> {
         }
         val logicalType = choiceMemento.getLogicalType();
         val spec = getCommonContext().getSpecificationLoader()
-                .lookupBySpecIdElseLoad(logicalType);
+                .specForLogicalType(logicalType)
+                .orElse(null);
 
         // support enums that are implementing an interface; only know this late in the day
         // TODO: this is a hack, really should push this deeper so that Encodeable OAMs also prefix themselves with their objectSpecId
diff --git a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ObjectAdapterMementoProviderForValueChoicesTest.java b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ObjectAdapterMementoProviderForValueChoicesTest.java
index da99254..8efe0f9 100644
--- a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ObjectAdapterMementoProviderForValueChoicesTest.java
+++ b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ObjectAdapterMementoProviderForValueChoicesTest.java
@@ -20,6 +20,7 @@ package org.apache.isis.viewer.wicket.ui.components.widgets.valuechoices;
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Optional;
 
 import org.jmock.Expectations;
 import org.jmock.auto.Mock;
@@ -82,8 +83,8 @@ public class ObjectAdapterMementoProviderForValueChoicesTest {
             allowing(mockCommonContext).getSpecificationLoader();
             will(returnValue(mockSpecificationLoader));
             
-            allowing(mockSpecificationLoader).lookupBySpecIdElseLoad(fakeLocalType);
-            will(returnValue(mockSpec));
+            allowing(mockSpecificationLoader).specForLogicalType(fakeLocalType);
+            will(returnValue(Optional.of(mockSpec)));
 
             allowing(mockSpec).isEncodeable();
             will(returnValue(true));
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapter.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapter.java
index b5c9c5e..dc1e2f4 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapter.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/ConverterForObjectAdapter.java
@@ -53,14 +53,11 @@ public class ConverterForObjectAdapter implements IConverter<ManagedObject> {
         val rootOid = RootOid.deStringEncoded(value);
         val spec = objectManager.getMetaModelContext()
                 .getSpecificationLoader()
-                .lookupBySpecIdElseLoad(rootOid.getLogicalTypeName());
+                .specForLogicalTypeNameElseFail(rootOid.getLogicalTypeName());
         
         val objectLoadRequest = ObjectLoader.Request.of(spec, rootOid.getIdentifier());
         
         return objectManager.loadObject(objectLoadRequest);
-        
-        // legacy of
-        //return getPersistenceSession().adapterFor(rootOid);
     }
 
     /**
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoWkt.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoWkt.java
index 260416e..62a94d0 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoWkt.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/mementos/ObjectMementoWkt.java
@@ -180,9 +180,12 @@ final class ObjectMementoWkt implements HasLogicalType, Serializable {
                     ObjectMementoWkt memento,
                     MetaModelContext mmc) {
 
-                ObjectSpecification objectSpec = mmc.getSpecificationLoader()
-                        .lookupBySpecIdElseLoad(memento.logicalType);
-                EncodableFacet encodableFacet = objectSpec.getFacet(EncodableFacet.class);
+                EncodableFacet encodableFacet = mmc.getSpecificationLoader()
+                        .specForLogicalType(memento.logicalType)
+                        .map(spec->spec.getFacet(EncodableFacet.class))
+                        .orElseThrow(()->_Exceptions.unrecoverableFormatted(
+                                "logical type %s is expected to have a EncodableFacet", memento.logicalType));
+                
                 return encodableFacet.fromEncodedString(memento.encodableValue);
             }
 
@@ -281,7 +284,7 @@ final class ObjectMementoWkt implements HasLogicalType, Serializable {
                     ObjectMementoWkt memento,
                     MetaModelContext mmc) {
                 ObjectSpecification spec = mmc.getSpecificationLoader()
-                        .lookupBySpecIdElseLoad(memento.logicalType);
+                        .specForLogicalTypeElseFail(memento.logicalType);
                 return mmc.getObjectManager().getObjectSerializer()
                         .deserialize(spec, memento.serializedObject);
             }
@@ -401,10 +404,9 @@ final class ObjectMementoWkt implements HasLogicalType, Serializable {
 
         // -- // TODO[2112] do we ever need to create ENCODEABLE here?
         val logicalTypeName = rootOid.getLogicalTypeName();
-        val spec = specificationLoader.lookupBySpecIdElseLoad(logicalTypeName);
-        if(spec==null) {
-            throw _Exceptions.unrecoverableFormatted("cannot recreate spec from logicalTypeName %s", logicalTypeName);
-        }
+        val spec = specificationLoader.specForLogicalTypeName(logicalTypeName)
+                .orElseThrow(()->_Exceptions.unrecoverableFormatted(
+                        "cannot recreate spec from logicalTypeName %s", logicalTypeName));
         
         this.cardinality = Cardinality.SCALAR;
         this.logicalType = spec.getLogicalType();
@@ -506,7 +508,7 @@ final class ObjectMementoWkt implements HasLogicalType, Serializable {
     ManagedObject reconstructObject(MetaModelContext mmc) {
 
         val specificationLoader = mmc.getSpecificationLoader();
-        val spec = specificationLoader.lookupBySpecIdElseLoad(logicalType);
+        val spec = specificationLoader.specForLogicalType(logicalType).orElse(null);
         if(spec==null) {
             // eg. ill-formed request
             return null;