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);
}
}