You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/05/30 15:58:10 UTC

[isis] branch master updated: ISIS-3063: adds support for aliased object and service spec lookup

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 395a7b38f8 ISIS-3063: adds support for aliased object and service spec lookup
395a7b38f8 is described below

commit 395a7b38f8df16f053a397497a7ea57fca3fce3c
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon May 30 17:58:05 2022 +0200

    ISIS-3063: adds support for aliased object and service spec lookup
---
 .../metamodel/specloader/LogicalTypeResolver.java  | 12 ++++-
 .../specloader/LogicalTypeResolverDefault.java     | 56 +++++++++++++++-------
 .../specloader/SpecificationLoaderDefault.java     | 22 ++++++---
 .../DomainModelTest_usingGoodDomain.java           | 22 +++++++++
 .../model/good/ProperServiceWithAlias.java         | 41 ++++++++++++++++
 5 files changed, 130 insertions(+), 23 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/LogicalTypeResolver.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/LogicalTypeResolver.java
index eea39ba348..720c70c20b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/LogicalTypeResolver.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/LogicalTypeResolver.java
@@ -47,13 +47,23 @@ interface LogicalTypeResolver {
     /**
      * Collects concrete types, ignores abstract types and interfaces.
      * Allows types to override their concrete super types.
+     * <p>
+     * Acts as an identity operator with side-effects.
      * @param spec - type's ObjectSpecification
      */
-    void register(@NonNull ObjectSpecification spec);
+    ObjectSpecification register(@NonNull ObjectSpecification spec);
 
     /**
      * Removes all entries from the lookup table.
      */
     void clear();
 
+    /**
+     * Collects aliases for concrete types, ignores abstract types and interfaces.
+     * <p>
+     * Acts as an identity operator with side-effects.
+     * @param spec - type's ObjectSpecification
+     */
+    ObjectSpecification registerAliases(@NonNull ObjectSpecification spec);
+
 }
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/LogicalTypeResolverDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/LogicalTypeResolverDefault.java
index 8f92d4d553..8336b837bd 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/LogicalTypeResolverDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/LogicalTypeResolverDefault.java
@@ -45,30 +45,33 @@ class LogicalTypeResolverDefault implements LogicalTypeResolver {
     }
 
     @Override
-    public void register(final @NonNull ObjectSpecification spec) {
+    public ObjectSpecification register(final @NonNull ObjectSpecification spec) {
+
+        val logicalTypeName = spec.getLogicalTypeName();
+
+        if(logicalTypeByName.containsKey(logicalTypeName)) {
+            return spec;
+        }
 
         // collect concrete classes (do not collect abstract or anonymous types or interfaces)
         if(!spec.isAbstract()
                 && hasTypeIdentity(spec)) {
 
-            val key = spec.getLogicalTypeName();
-
-            val previousMapping = logicalTypeByName.put(key, spec.getLogicalType());
-
-            if(previousMapping!=null) {
+            putWithWarnOnOverride(logicalTypeName, spec);
+        }
+        return spec;
+    }
 
-                val msg = String.format("Overriding existing mapping\n"
-                        + "%s -> %s,\n"
-                        + "with\n "
-                        + "%s -> %s\n "
-                        + "This will result in the meta-model validation to fail.",
-                        key, previousMapping.getCorrespondingClass(),
-                        key, spec.getCorrespondingClass());
+    @Override
+    public ObjectSpecification registerAliases(final @NonNull ObjectSpecification spec) {
 
-                log.warn(msg);
+        // adding aliases to the lookup map
+        spec.getAliases()
+        .forEach(alias->{
+                putWithWarnOnOverride(alias.getLogicalTypeName(), spec);
+        });
 
-            }
-        }
+        return spec;
     }
 
     // -- HELPER
@@ -79,4 +82,25 @@ class LogicalTypeResolverDefault implements LogicalTypeResolver {
         return spec.getCorrespondingClass().getCanonicalName()!=null;
     }
 
+    private void putWithWarnOnOverride(
+            final String logicalTypeName,
+            final ObjectSpecification spec) {
+
+        final LogicalType previousMapping =
+                logicalTypeByName.put(logicalTypeName, spec.getLogicalType());
+
+        if(previousMapping!=null
+                && !spec.getLogicalType().equals(previousMapping)) {
+            val msg = String.format("Overriding existing mapping\n"
+                    + "%s -> %s,\n"
+                    + "with\n "
+                    + "%s -> %s\n "
+                    + "This will result in the meta-model validation to fail.",
+                    logicalTypeName, previousMapping.getCorrespondingClass(),
+                    logicalTypeName, spec.getCorrespondingClass());
+            log.warn(msg);
+        }
+
+    }
+
 }
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 6de0a67735..e1193ad8f6 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
@@ -450,7 +450,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
             return logicalType;
         }
 
-        //TODO[2533] if the logicalTypeName is not available and instead a fqcn was passed in, that should also be supported
+        //XXX[2533] if the logicalTypeName is not available and instead a fqcn was passed in, that should also be supported
 
         // falling back assuming the logicalTypeName equals the fqn of the corresponding class
         // which might not always be true,
@@ -558,14 +558,24 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 
         val substitutedType = substitute.apply(type);
 
-        val spec = cache.computeIfAbsent(substitutedType, __->{
-            val newSpec = createSpecification(beanClassifier.apply(substitutedType));
-            logicalTypeResolver.register(newSpec);
-            return newSpec;
-        });
+        val spec = cache.computeIfAbsent(substitutedType, _spec->
+            logicalTypeResolver
+                .register(
+                        createSpecification(beanClassifier.apply(substitutedType))));
 
         spec.introspectUpTo(upTo);
 
+        if(spec.getAliases().isNotEmpty()) {
+            //XXX[3063] hitting this a couple of times (~10) per spec (with aliases)
+            // even though already registered;
+            // room for performance optimizations, but at the time of writing
+            // don't want to add a ObjectSpecification flag to keep track of alias registered state;
+            // as an alternative purge the aliased facets and introspect aliased attributes from annotations
+            // much earlier in the bootstrap process, same as we do with @Named processing
+            logicalTypeResolver
+                .registerAliases(spec);
+        }
+
         return spec;
     }
 
diff --git a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
index 2f2cc35eda..aefd5b42b8 100644
--- a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
+++ b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
@@ -41,6 +41,7 @@ import static org.junit.jupiter.api.Assertions.fail;
 
 import org.apache.isis.applib.annotation.Introspection.EncapsulationPolicy;
 import org.apache.isis.applib.annotation.Introspection.MemberAnnotationPolicy;
+import org.apache.isis.applib.id.LogicalType;
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.services.metamodel.BeanSort;
 import org.apache.isis.applib.services.metamodel.Config;
@@ -84,6 +85,7 @@ import org.apache.isis.testdomain.model.good.ProperMemberInheritance_usingAbstra
 import org.apache.isis.testdomain.model.good.ProperMemberInheritance_usingInterface;
 import org.apache.isis.testdomain.model.good.ProperMemberSupport;
 import org.apache.isis.testdomain.model.good.ProperMemberSupportDiscovery;
+import org.apache.isis.testdomain.model.good.ProperServiceWithAlias;
 import org.apache.isis.testdomain.model.good.ProperServiceWithMixin;
 import org.apache.isis.testdomain.model.good.ViewModelWithAnnotationOptionalUsingPrivateSupport;
 import org.apache.isis.testdomain.model.good.ViewModelWithEncapsulatedMembers;
@@ -508,6 +510,26 @@ class DomainModelTest_usingGoodDomain {
 
     }
 
+    @Test
+    void aliasesOnDomainServices_shouldBeHonored() {
+
+        val objectSpec = specificationLoader.specForTypeElseFail(ProperServiceWithAlias.class);
+        assertTrue(objectSpec.isInjectable());
+        assertTrue(objectSpec.getAction("now").isPresent());
+
+        assertEquals(Can.of(
+                "testdomain.v1.ProperServiceWithAlias",
+                "testdomain.v2.ProperServiceWithAlias"),
+                objectSpec.getAliases().map(LogicalType::getLogicalTypeName));
+
+        assertEquals(objectSpec,
+                specificationLoader.specForLogicalTypeName("testdomain.v1.ProperServiceWithAlias")
+                .orElse(null));
+        assertEquals(objectSpec,
+                specificationLoader.specForLogicalTypeName("testdomain.v2.ProperServiceWithAlias")
+                .orElse(null));
+    }
+
     @Test
     void viewmodelWithEncapsulatedMembers() {
 
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperServiceWithAlias.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperServiceWithAlias.java
new file mode 100644
index 0000000000..2735c2090c
--- /dev/null
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperServiceWithAlias.java
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.testdomain.model.good;
+
+import javax.inject.Named;
+
+import org.joda.time.LocalDateTime;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+
+@Named("testdomain.ProperServiceWithAlias")
+@DomainService(nature = NatureOfService.VIEW,
+        aliased = {
+            "testdomain.v1.ProperServiceWithAlias",
+            "testdomain.v2.ProperServiceWithAlias",
+        })
+public class ProperServiceWithAlias {
+
+    @Action public LocalDateTime now() {
+        return LocalDateTime.now();
+    }
+
+}