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/06/07 07:45:43 UTC

[isis] branch master updated: ISIS-2721: properly detect and register value types

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 bf521d3  ISIS-2721: properly detect and register value types
bf521d3 is described below

commit bf521d310809f3d4727b35a8b5493bfbce8a5be3
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Jun 7 09:45:27 2021 +0200

    ISIS-2721: properly detect and register value types
    
    fixes Blob/Clob not detected as value types
---
 .../java/org/apache/isis/applib/value/Clob.java    |  3 +-
 .../commons/internal/functions/_Predicates.java    |  8 ++++
 .../IsisBeanFactoryPostProcessorForSpring.java     |  3 +-
 .../core/config/beans/IsisBeanTypeClassifier.java  | 32 +++++++++++++---
 .../config/beans/IsisBeanTypeClassifierImpl.java   | 24 ++++++++++--
 .../core/config/beans/IsisBeanTypeRegistry.java    |  2 +
 .../config/beans/IsisBeanTypeRegistryDefault.java  |  2 +
 .../beans/IsisComponentScanInterceptorImpl.java    |  2 +-
 .../specloader/SpecificationLoaderDefault.java     | 44 ++++++++++++++--------
 .../jdo/metamodel/beans/JdoBeanTypeClassifier.java |  6 ++-
 10 files changed, 97 insertions(+), 29 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/value/Clob.java b/api/applib/src/main/java/org/apache/isis/applib/value/Clob.java
index 37d3e2a..cfb8b5e 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/value/Clob.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/value/Clob.java
@@ -36,7 +36,6 @@ import org.apache.isis.applib.jaxb.PrimitiveJaxbAdapters;
 import org.apache.isis.commons.internal.base._Strings;
 
 import lombok.val;
-import lombok.extern.log4j.Log4j2;
 
 /**
  * Represents a character large object.
@@ -64,7 +63,7 @@ import lombok.extern.log4j.Log4j2;
         "org.apache.isis.core.metamodel.facets.value.clobs.ClobValueSemanticsProvider")
 @DomainObject(logicalTypeName = IsisModuleApplib.NAMESPACE + ".value.Clob")
 @XmlJavaTypeAdapter(Clob.JaxbToStringAdapter.class)   // for JAXB view model support
-@Log4j2
+//@Log4j2
 public final class Clob implements NamedWithMimeType {
 
     /**
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Predicates.java b/commons/src/main/java/org/apache/isis/commons/internal/functions/_Predicates.java
index bf2a148..62b9b62 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Predicates.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/functions/_Predicates.java
@@ -62,6 +62,14 @@ public final class _Predicates {
     }
 
     /**
+    *
+    * @return a Predicate that always tests false
+    */
+   public static <T> Predicate<T> alwaysFalse() {
+       return __->false;
+   }
+
+    /**
      * @return a Predicate that tests for the operand to be not null
      */
     public static <T> Predicate<T> isPresent() {
diff --git a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanFactoryPostProcessorForSpring.java b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanFactoryPostProcessorForSpring.java
index dc984cf..f7cbe38 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanFactoryPostProcessorForSpring.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanFactoryPostProcessorForSpring.java
@@ -67,8 +67,7 @@ implements
 
     @Override
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-        val environment = applicationContext.getEnvironment();
-        isisBeanTypeClassifier = IsisBeanTypeClassifier.createInstance(environment);
+        isisBeanTypeClassifier = IsisBeanTypeClassifier.createInstance(applicationContext);
         isisComponentScanInterceptor = IsisComponentScanInterceptor.createInstance(isisBeanTypeClassifier);
     }
 
diff --git a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifier.java b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifier.java
index f3b7703..9e1335b 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifier.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifier.java
@@ -18,7 +18,12 @@
  */
 package org.apache.isis.core.config.beans;
 
-import org.springframework.core.env.Environment;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import javax.annotation.Nullable;
+
+import org.springframework.context.ApplicationContext;
 
 import org.apache.isis.applib.services.metamodel.BeanSort;
 import org.apache.isis.commons.collections.Can;
@@ -39,10 +44,11 @@ public interface IsisBeanTypeClassifier {
      * Returns the bean classification for given {@code type}.
      *
      * @apiNote Initially used to collect all concrete types that are considered by Spring
-     * for type inspection, but later used by the {@code SpecificationLoader} to also
+     * for type inspection, most likely without any {@code context} yet being available,
+     * but later used by the {@code SpecificationLoader} to also
      * classify non-concrete types (interfaces and abstract classes).
      */
-    BeanClassification classify(Class<?> type);
+    BeanClassification classify(Class<?> type, @Nullable BeanClassificationContext context);
 
     // -- FACTORY
 
@@ -53,8 +59,9 @@ public interface IsisBeanTypeClassifier {
         return new IsisBeanTypeClassifierImpl(Can.empty());
     }
 
-    static IsisBeanTypeClassifier createInstance(final @NonNull Environment environment) {
-        return new IsisBeanTypeClassifierImpl(Can.ofArray(environment.getActiveProfiles()));
+    static IsisBeanTypeClassifier createInstance(final @NonNull ApplicationContext applicationContext) {
+        return new IsisBeanTypeClassifierImpl(
+                Can.ofArray(applicationContext.getEnvironment().getActiveProfiles()));
     }
 
     // -- LOOKUP
@@ -63,6 +70,17 @@ public interface IsisBeanTypeClassifier {
         return Can.ofCollection(_Plugin.loadAll(IsisBeanTypeClassifier.class));
     }
 
+    // -- BEAN CLASSIFICATION CONTEXT
+
+    @Value
+    public static class BeanClassificationContext {
+        private final @NonNull Predicate<Class<?>> isRegisteredValueType;
+    }
+
+    static BeanClassificationContext newContext(final Set<Class<?>> registeredValueTypes) {
+        return new BeanClassificationContext(registeredValueTypes::contains);
+    }
+
     // -- BEAN CLASSIFICATION RESULT
 
     @Value(staticConstructor = "of")
@@ -92,4 +110,8 @@ public interface IsisBeanTypeClassifier {
 
     }
 
+
+
+
+
 }
diff --git a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierImpl.java b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierImpl.java
index 1dba6f2..48bf001 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierImpl.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierImpl.java
@@ -23,6 +23,7 @@ import java.lang.reflect.Modifier;
 import java.util.Collection;
 import java.util.Locale;
 
+import javax.annotation.Nullable;
 import javax.enterprise.inject.Vetoed;
 import javax.persistence.Entity;
 import javax.persistence.Table;
@@ -54,7 +55,9 @@ implements IsisBeanTypeClassifier {
     private final Can<IsisBeanTypeClassifier> classifierPlugins = IsisBeanTypeClassifier.get();
 
     @Override
-    public BeanClassification classify(final @NonNull Class<?> type) {
+    public BeanClassification classify(
+            final @NonNull Class<?> type,
+            final @Nullable BeanClassificationContext beanClassificationContext) {
 
         // handle arbitrary types ...
 
@@ -80,7 +83,7 @@ implements IsisBeanTypeClassifier {
             return BeanClassification.delegated(BeanSort.ABSTRACT);
         }
 
-        // handle actual bean types ...
+        // handle vetoing ...
 
         if(findNearestAnnotation(type, Vetoed.class).isPresent()
                 || findNearestAnnotation(type, Programmatic.class).isPresent()) {
@@ -95,6 +98,21 @@ implements IsisBeanTypeClassifier {
             return BeanClassification.selfManaged(BeanSort.VETOED); // reject
         }
 
+        // handle value types ...
+
+        if(beanClassificationContext!=null
+                && beanClassificationContext.getIsRegisteredValueType().test(type)) {
+            return BeanClassification.delegated(BeanSort.VALUE);
+        }
+
+        val aValue = findNearestAnnotation(type, org.apache.isis.applib.annotation.Value.class)
+                .orElse(null);
+        if(aValue!=null) {
+            return BeanClassification.delegated(BeanSort.VALUE);
+        }
+
+        // handle actual bean types ...
+
         val aDomainService = findNearestAnnotation(type, DomainService.class);
         if(aDomainService.isPresent()) {
             return BeanClassification
@@ -104,7 +122,7 @@ implements IsisBeanTypeClassifier {
 
         // allow ServiceLoader plugins to have a say, eg. when classifying entity types
         for(val classifier : classifierPlugins) {
-            val classification = classifier.classify(type);
+            val classification = classifier.classify(type, beanClassificationContext);
             if(classification!=null) {
                 return classification;
             }
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 f58751e..168c239 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
@@ -36,6 +36,8 @@ public interface IsisBeanTypeRegistry {
     Set<Class<?>> getEntityTypes();
     Set<Class<?>> getMixinTypes();
     Set<Class<?>> getViewModelTypes();
+    /** discovered per {@code @Value} annotation (vs. registered using ValueTypeResgistry)*/
+    Set<Class<?>> getDiscoveredValueTypes();
 
     // -- LOOKUPS
 
diff --git a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeRegistryDefault.java b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeRegistryDefault.java
index ea91ada..684ec1a 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeRegistryDefault.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeRegistryDefault.java
@@ -63,6 +63,7 @@ public class IsisBeanTypeRegistryDefault implements IsisBeanTypeRegistry {
     @Getter(onMethod_ = {@Override}) private final Set<Class<?>> entityTypes = new HashSet<>();
     @Getter(onMethod_ = {@Override}) private final Set<Class<?>> mixinTypes = new HashSet<>();
     @Getter(onMethod_ = {@Override}) private final Set<Class<?>> viewModelTypes = new HashSet<>();
+    @Getter(onMethod_ = {@Override}) private final Set<Class<?>> discoveredValueTypes = new HashSet<>();
 
     // -- LOOKUPS
 
@@ -108,6 +109,7 @@ public class IsisBeanTypeRegistryDefault implements IsisBeanTypeRegistry {
             case MANAGED_BEAN_NOT_CONTRIBUTING:
             case COLLECTION:
             case VALUE:
+                discoveredValueTypes.add(cls);
             case ABSTRACT: // <-- unexpected code reach
             case VETOED:
             case UNKNOWN:
diff --git a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisComponentScanInterceptorImpl.java b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisComponentScanInterceptorImpl.java
index acdc89c..55dd225 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisComponentScanInterceptorImpl.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisComponentScanInterceptorImpl.java
@@ -79,7 +79,7 @@ implements IsisComponentScanInterceptor {
         }
 
         val type = classOrFailure.getUnderlyingClass();
-        val classification = isisBeanTypeClassifier.classify(type);
+        val classification = isisBeanTypeClassifier.classify(type, /*BeanClassificationContext*/null);
 
         val delegated = classification.isDelegateLifecycleManagement();
         typeMeta.setInjectable(delegated);
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 0ef398e..34eea97 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
@@ -20,12 +20,15 @@ package org.apache.isis.core.metamodel.specloader;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Optional;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import javax.annotation.Nullable;
 import javax.annotation.PostConstruct;
@@ -49,6 +52,7 @@ import org.apache.isis.commons.internal.base._Blackhole;
 import org.apache.isis.commons.internal.base._Lazy;
 import org.apache.isis.commons.internal.base._Timing;
 import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.core.config.beans.IsisBeanMetaData;
@@ -114,6 +118,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
     private final IsisBeanTypeRegistry isisBeanTypeRegistry;
     private final ClassSubstitutorRegistry classSubstitutorRegistry;
     private final ValueTypeRegistry valueTypeRegistry;
+    private final IsisBeanTypeClassifier.BeanClassificationContext beanClassificationContext;
 
     private final ProgrammingModel programmingModel;
     private final PostProcessor postProcessor;
@@ -171,6 +176,10 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         this.isisBeanTypeRegistry = isisBeanTypeRegistry;
         this.valueTypeRegistry = valueTypeRegistry;
         this.classSubstitutorRegistry = classSubstitutorRegistry;
+        this.beanClassificationContext = IsisBeanTypeClassifier
+                .newContext(valueTypeRegistry
+                        .streamRegisteredClasses()
+                        .collect(Collectors.toCollection(HashSet::new)));
     }
 
     /** JUnit Test Support */
@@ -230,18 +239,21 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         // (an optimization, not strictly required)
         loadSpecifications(ApplicationFeatureSort.class/*, ...*/);
 
-        log.info(" - adding types from ValueTypeProviders");
+        log.info(" - adding value types from from class-path scan and ValueTypeProviders");
 
-        val valueTypeSpecs = _Lists.<ObjectSpecification>newArrayList();
+        val valueTypeSpecs = _Maps.<Class<?>, ObjectSpecification>newHashMap();
 
-        valueTypeRegistry.streamRegisteredClasses()
-        .forEach(cls -> {
-            val spec = loadSpecification(cls, IntrospectionState.NOT_INTROSPECTED);
-            if(spec!=null) {
-                knownSpecs.add(spec);
-                valueTypeSpecs.add(spec);
-            }
-        });
+        Stream
+            .concat(
+                isisBeanTypeRegistry.getDiscoveredValueTypes().stream(),
+                valueTypeRegistry.streamRegisteredClasses())
+            .forEach(valueType -> {
+                val valueSpec = loadSpecification(valueType, IntrospectionState.NOT_INTROSPECTED);
+                if(valueSpec!=null) {
+                    knownSpecs.add(valueSpec);
+                    valueTypeSpecs.put(valueType, valueSpec);
+                }
+            });
 
         log.info(" - categorizing types from class-path scan");
 
@@ -278,12 +290,12 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         introspect(Can.ofCollection(knownSpecs), IntrospectionState.TYPE_INTROSPECTED);
 
         log.info(" - introspecting {} value types", valueTypeSpecs.size());
-        introspect(Can.ofCollection(valueTypeSpecs), IntrospectionState.FULLY_INTROSPECTED);
+        introspect(Can.ofCollection(valueTypeSpecs.values()), IntrospectionState.FULLY_INTROSPECTED);
 
         log.info(" - introspecting {} mixins", isisBeanTypeRegistry.getMixinTypes().size());
         introspect(Can.ofCollection(mixinSpecs), IntrospectionState.FULLY_INTROSPECTED);
 
-        log.info(" - introspecting {} managed beans contributing (aka domain services)", isisBeanTypeRegistry.getManagedBeansContributing().size());
+        log.info(" - introspecting {} managed beans contributing (domain services)", isisBeanTypeRegistry.getManagedBeansContributing().size());
 //        log.info(" - introspecting {}/{} entities (JDO/JPA)",
 //                isisBeanTypeRegistry.getEntityTypesJdo().size(),
 //                isisBeanTypeRegistry.getEntityTypesJpa().size());
@@ -378,7 +390,8 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
                 __->isisBeanTypeRegistry
                     .lookupIntrospectableType(type)
                     .map(IsisBeanMetaData::getBeanSort)
-                    .orElseGet(()->isisBeanTypeClassifier.classify(type).getBeanSort()),
+                    .orElseGet(()->isisBeanTypeClassifier.classify(type, beanClassificationContext)
+                            .getBeanSort()),
                 upTo);
     }
 
@@ -557,8 +570,9 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         if(isMetamodelFullyIntrospected()
                 && isisConfiguration.getCore().getMetaModel().getIntrospector().isLockAfterFullIntrospection()) {
 
-            val category = isisBeanTypeClassifier.classify(cls);
-            val sort = category.getBeanSort();
+            val sort = isisBeanTypeClassifier
+                    .classify(cls, beanClassificationContext)
+                    .getBeanSort();
 
 //          ISIS-2256:
 //            throw _Exceptions.illegalState(
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/isis/persistence/jdo/metamodel/beans/JdoBeanTypeClassifier.java b/persistence/jdo/metamodel/src/main/java/org/apache/isis/persistence/jdo/metamodel/beans/JdoBeanTypeClassifier.java
index 84edc36..8d31c09 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/isis/persistence/jdo/metamodel/beans/JdoBeanTypeClassifier.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/isis/persistence/jdo/metamodel/beans/JdoBeanTypeClassifier.java
@@ -20,6 +20,7 @@ package org.apache.isis.persistence.jdo.metamodel.beans;
 
 import java.util.Locale;
 
+import javax.annotation.Nullable;
 import javax.jdo.annotations.EmbeddedOnly;
 
 import org.apache.isis.applib.annotation.DomainObject;
@@ -40,7 +41,9 @@ import lombok.val;
 public class JdoBeanTypeClassifier implements IsisBeanTypeClassifier {
 
     @Override
-    public BeanClassification classify(Class<?> type) {
+    public BeanClassification classify(
+            final Class<?> type,
+            final @Nullable BeanClassificationContext context) {
 
         val persistenceCapableAnnot = findNearestAnnotation(type, javax.jdo.annotations.PersistenceCapable.class);
         if(persistenceCapableAnnot.isPresent()) {
@@ -86,4 +89,5 @@ public class JdoBeanTypeClassifier implements IsisBeanTypeClassifier {
         return null; // we don't feel responsible to classify given type
     }
 
+
 }