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/07/15 14:02:56 UTC

[isis] 01/18: ISIS-2403: specloader: fixes re-validation deadlocks

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

commit c3e79019d80147ad8397bf7323c6211bc9754316
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Jul 14 08:13:31 2020 +0200

    ISIS-2403: specloader: fixes re-validation deadlocks
---
 .../dflt/ProgrammingModelFacetsJava8.java          |  5 ++
 .../metamodel/specloader/SpecificationLoader.java  |  7 ++-
 .../specloader/SpecificationLoaderDefault.java     | 56 +++++++++++++++++-----
 .../specimpl/ObjectSpecificationAbstract.java      |  2 +-
 4 files changed, 57 insertions(+), 13 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java
index 53277ff..971656f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java
@@ -371,6 +371,11 @@ public final class ProgrammingModelFacetsJava8 extends ProgrammingModelAbstract
         addValidator(new TitlesAndTranslationsValidator());
 
         addValidator((objectSpec, validator) -> {
+            
+            if(Object.class.equals(objectSpec.getCorrespondingClass())) {
+                return false;
+            }
+            
             final long numActions = objectSpec.streamObjectActions(Contributed.INCLUDED).count();
             if (numActions > 0L) {
                 
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 0a12ce0..862fe84 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
@@ -108,6 +108,11 @@ public interface SpecificationLoader {
 
     Class<?> lookupType(ObjectSpecId objectSpecId);
 
+    /**
+     * queue {@code objectSpec} for later validation
+     * @param objectSpec
+     */
+    void validateLater(ObjectSpecification objectSpec);
 
     // -- SHORTCUTS
 
@@ -133,5 +138,5 @@ public interface SpecificationLoader {
     }
 
 
-    void revalidateIfNecessary();
+    
 }
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 61674fa..c06d083 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
@@ -21,6 +21,9 @@ 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;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 
 import javax.annotation.Nullable;
@@ -51,6 +54,7 @@ import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModelService;
 import org.apache.isis.core.metamodel.progmodels.dflt.ProgrammingModelFacetsJava8;
+import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureType;
 import org.apache.isis.core.metamodel.services.classsubstitutor.ClassSubstitutor;
 import org.apache.isis.core.metamodel.services.classsubstitutor.ClassSubstitutor.Substitution;
 import org.apache.isis.core.metamodel.services.classsubstitutor.ClassSubstitutorDefault;
@@ -207,6 +211,10 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         val knownSpecs = _Lists.<ObjectSpecification>newArrayList();
 
         val stopWatch = _Timing.now();
+        
+        //XXX[ISIS-2403] these classes only get discovered by validators, so just preload their specs
+        // (an optimization, not strictly required)
+        loadSpecifications(ApplicationFeatureType.class/*, ...*/);
 
         log.info(" - adding types from ValueTypeProviders");
 
@@ -289,7 +297,14 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
     private _Lazy<ValidationFailures> validationResult = 
             _Lazy.threadSafe(this::collectFailuresFromMetaModel);
 
+    private final AtomicBoolean validationInProgress = new AtomicBoolean(false);
+    
+    private final BlockingQueue<ObjectSpecification> validationQueue = new LinkedBlockingQueue<>();
+    
     private ValidationFailures collectFailuresFromMetaModel() {
+        //XXX while doing this don't trigger revalidateIfNecessary on self
+        //otherwise will deadlock
+        validationInProgress.set(true);               
         val failures = new ValidationFailures();
         programmingModel.streamValidators()
         .map(MetaModelValidatorAbstract.class::cast)
@@ -305,6 +320,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
             }
         });
         log.debug("Done");
+        validationInProgress.set(false);
         return failures;
     }
 
@@ -384,22 +400,40 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         return cachedSpec;
     }
 
-    public void revalidateIfNecessary() {
+    @Override
+    public void validateLater(ObjectSpecification objectSpec) {
         if(!isMetamodelFullyIntrospected()) {
-            return;
+            // don't trigger validation during bootstrapping
+            // getValidationResult() is lazily populated later on first request anyway
+            return; 
         }
-        if(!this.isisConfiguration.getCore().getMetaModel().getIntrospector().isValidateIncrementally()) {
+        if(!isisConfiguration.getCore().getMetaModel().getIntrospector().isValidateIncrementally()) {
+            // re-validation after the initial one can be turned off by means of above config option
             return;
         }
-
-        if (this.validationResult.isMemoized()) {
-            this.validationResult.clear();
-            final ValidationFailures validationFailures = this.getValidationResult();
-
-            if(validationFailures.hasFailures()) {
-                throw _Exceptions.illegalState(String.join("\n", validationFailures.getMessages("[%d] %s")));
-            }
+        
+        log.info("re-validation triggered by {}", objectSpec);
+        
+        // validators might discover new specs 
+        // to prevent deadlocks, we queue up validation requests to be processed later
+        if(validationInProgress.get()) {
+            validationQueue.offer(objectSpec);
+            return; 
+        }
+        
+        while(validationQueue.poll()!=null) {
+            // keep re-validating until the queue is empty
+            validationResult.clear();
+            getValidationResult();            
         }
+        
+        // only after things have settled we offer feedback to the user (interface) 
+        
+        final ValidationFailures validationFailures = getValidationResult();
+        if(validationFailures.hasFailures()) {
+            throw _Exceptions.illegalState(String.join("\n", validationFailures.getMessages("[%d] %s")));
+        }
+        
     }
 
     // -- LOOKUP
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index fbe427b..a8fe145 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -318,7 +318,7 @@ implements ObjectSpecification {
         }
 
         if(revalidate) {
-            getSpecificationLoader().revalidateIfNecessary();
+            getSpecificationLoader().validateLater(this);
         }
     }