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 2019/04/04 12:54:11 UTC

[isis] branch 2033-IoC_spring updated: ISIS-2033: make SpecLoader's specs snapshot thread-safe

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 2033-IoC_spring
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/2033-IoC_spring by this push:
     new f1398a0  ISIS-2033: make SpecLoader's specs snapshot thread-safe
f1398a0 is described below

commit f1398a024b07a65049e727ab01401b77f0f896e5
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Apr 4 14:54:04 2019 +0200

    ISIS-2033: make SpecLoader's specs snapshot thread-safe
    
    Task-Url: https://issues.apache.org/jira/browse/ISIS-2033
---
 .../domainobject/DomainObjectAnnotationFacetFactory.java   |  2 +-
 .../appfeat/ApplicationFeatureRepositoryDefault.java       |  2 +-
 .../metamodel/services/layout/LayoutServiceDefault.java    |  2 +-
 .../metamodel/services/metamodel/MetaModelExporter.java    |  2 +-
 .../services/metamodel/MetaModelServiceDefault.java        |  2 +-
 .../metamodel/services/swagger/internal/Generation.java    |  4 ++--
 .../core/metamodel/specloader/SpecificationLoader.java     | 11 ++++++-----
 .../metamodel/specloader/SpecificationLoaderDefault.java   | 14 +++++++++++---
 .../specloader/validator/MetaModelValidatorVisiting.java   |  2 +-
 .../appfeat/ApplicationFeatureRepositoryDefaultTest.java   |  2 +-
 .../runtime/system/session/IsisSessionFactoryDefault.java  |  2 +-
 .../server/resources/DomainTypeResourceServerside.java     |  2 +-
 .../server/resources/HomePageReprRenderer.java             |  2 +-
 .../incubator/command/IncubatingSpecificationLoader.java   |  8 +-------
 14 files changed, 30 insertions(+), 27 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
index 7fbf89d..c89112a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
@@ -517,7 +517,7 @@ implements MetaModelValidatorRefiner, PostConstructMethodCache, ObjectSpecIdFace
                 }
 
                 final Map<ObjectSpecId, ObjectSpecification> specById = _Maps.newHashMap();
-                for (final ObjectSpecification otherSpec : getSpecificationLoader().allSpecifications()) {
+                for (final ObjectSpecification otherSpec : getSpecificationLoader().currentSpecifications()) {
 
                     if(thisSpec == otherSpec) {
                         continue;
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
index fa2cf6f..5997123 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
@@ -106,7 +106,7 @@ public class ApplicationFeatureRepositoryDefault implements ApplicationFeatureRe
     private Collection<ObjectSpecification> primeMetaModel() {
         serviceRegistry.streamServices()
             .forEach(service->specificationLoader.loadSpecification(service.getClass()));
-        return specificationLoader.allSpecifications();
+        return specificationLoader.currentSpecifications();
     }
 
     private void createApplicationFeaturesFor(final Collection<ObjectSpecification> specifications) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
index 6485d20..82945ed 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
@@ -84,7 +84,7 @@ public class LayoutServiceDefault implements LayoutService {
 
     @Override
     public byte[] toZip(final Style style) {
-        final Collection<ObjectSpecification> allSpecs = specificationLoader.allSpecifications();
+        final Collection<ObjectSpecification> allSpecs = specificationLoader.currentSpecifications();
         final List<ObjectSpecification> domainObjectSpecs = _Lists
                 .filter(allSpecs, spec ->
                         !spec.isAbstract() &&
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java
index b312bab..67d475f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java
@@ -80,7 +80,7 @@ class MetaModelExporter {
         // phase 1: create a domainClassType for each ObjectSpecification
         // these are added into a map for lookups in phase 2
         final Map<ObjectSpecification, DomainClassDto> domainClassByObjectSpec = _Maps.newHashMap();
-        for (final ObjectSpecification specification : specificationLoader.allSpecifications()) {
+        for (final ObjectSpecification specification : specificationLoader.currentSpecifications()) {
             DomainClassDto domainClassType = asXsdType(specification, config);
             domainClassByObjectSpec.put(specification, domainClassType);
         }
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 1b2b0c5..bfdb765 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
@@ -101,7 +101,7 @@ public class MetaModelServiceDefault implements MetaModelService {
     @Override
     public DomainModel getDomainModel() {
 
-        final Collection<ObjectSpecification> specifications = specificationLoader.allSpecifications();
+        final Collection<ObjectSpecification> specifications = specificationLoader.currentSpecifications();
 
         final List<DomainMember> rows = _Lists.newArrayList();
         for (final ObjectSpecification spec : specifications) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
index 1355f48..1cc522b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
@@ -128,7 +128,7 @@ class Generation {
     void appendServicePathsAndDefinitions() {
         // (previously we took a protective copy to avoid a concurrent modification exception,
         // but this is now done by SpecificationLoader itself)
-        for (final ObjectSpecification objectSpec : specificationLoader.allSpecifications()) {
+        for (final ObjectSpecification objectSpec : specificationLoader.currentSpecifications()) {
 
             final DomainServiceFacet domainServiceFacet = objectSpec.getFacet(DomainServiceFacet.class);
             if (domainServiceFacet == null) {
@@ -181,7 +181,7 @@ class Generation {
     void appendObjectPathsAndDefinitions() {
         // (previously we took a protective copy to avoid a concurrent modification exception,
         // but this is now done by SpecificationLoader itself)
-        for (final ObjectSpecification objectSpec : specificationLoader.allSpecifications()) {
+        for (final ObjectSpecification objectSpec : specificationLoader.currentSpecifications()) {
         	
         	probe.println("objectSpec %s", objectSpec);
 
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 027118e..9633f90 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
@@ -17,13 +17,13 @@
 package org.apache.isis.core.metamodel.specloader;
 
 import java.util.List;
+import java.util.stream.Stream;
 
 import javax.annotation.Nullable;
 
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.config.property.ConfigPropertyBoolean;
 import org.apache.isis.config.property.ConfigPropertyEnum;
-import org.apache.isis.core.commons.exceptions.IsisException;
 import org.apache.isis.core.commons.lang.ClassUtil;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -53,15 +53,16 @@ public interface SpecificationLoader {
     // -- LOOKUP
     
 	/**
-     * Returns (a new list holding a copy of) all the loaded specifications.
-     *
+	 * @ThreadSafe
      * <p>
-     *     A new list is returned to avoid concurrent modification exceptions for if the caller then
+     *     Must be implemented thread-safe to avoid concurrent modification exceptions for if the caller
      *     iterates over all the specifications and performs an activity that might give rise to new
      *     ObjectSpec's being discovered, eg performing metamodel validation.
      * </p>
+     * 
+     * @return snapshot of all the (currently) loaded specifications
      */
-	List<ObjectSpecification> allSpecifications();
+	List<ObjectSpecification> currentSpecifications();
 
 	ObjectSpecification lookupBySpecId(ObjectSpecId objectSpecId);
 	
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 a6ead9c..8a40193 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
@@ -2,6 +2,7 @@ package org.apache.isis.core.metamodel.specloader;
 
 import static org.apache.isis.commons.internal.base._With.requires;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Callable;
@@ -285,7 +286,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 		}
 
 		//TODO[2033] don't block on long running code ... 'specSpi.introspectUpTo(upTo);'
-		synchronized (this) {
+		synchronized (cache) {
 			
 			spec = cache.get(typeName);
 			if (spec != null) {
@@ -308,8 +309,15 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 	// -- LOOKUP
 
 	@Override
-	public List<ObjectSpecification> allSpecifications() {
-		return _Lists.newArrayList(allCachedSpecifications());
+	public List<ObjectSpecification> currentSpecifications() {
+		
+		final List<ObjectSpecification> defensiveCopy;
+		
+		synchronized (cache) {
+			defensiveCopy = _Lists.newArrayList(allCachedSpecifications());
+		}
+		
+		return defensiveCopy;
 	}
 
 	@Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorVisiting.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorVisiting.java
index 42e2471..7ee773a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorVisiting.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorVisiting.java
@@ -67,7 +67,7 @@ public class MetaModelValidatorVisiting extends MetaModelValidatorAbstract {
         // all currently known specs
         // (previously we took a protective copy to avoid a concurrent modification exception,
         // but this is now done by SpecificationLoader itself)
-        final Collection<ObjectSpecification> specsToValidate = getSpecificationLoader().allSpecifications();
+        final Collection<ObjectSpecification> specsToValidate = getSpecificationLoader().currentSpecifications();
 
         // don't validate any specs already processed
         specsToValidate.removeAll(specsAlreadyValidated);
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefaultTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefaultTest.java
index b376ba7..d8737da 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefaultTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefaultTest.java
@@ -269,7 +269,7 @@ public class ApplicationFeatureRepositoryDefaultTest {
                 allowing(mockServiceRegistry).streamServices();
                 will(returnValue(_Lists.newArrayList().stream()));
 
-                allowing(mockSpecificationLoader).allSpecifications();
+                allowing(mockSpecificationLoader).currentSpecifications();
                 will(returnValue(_Lists.newArrayList()));
             }});
 
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryDefault.java
index 2e62f8f..fe2e969 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryDefault.java
@@ -172,7 +172,7 @@ public class IsisSessionFactoryDefault implements IsisSessionFactory {
 
             // (previously we took a protective copy to avoid a concurrent modification exception,
             // but this is now done by SpecificationLoader itself)
-            for (final ObjectSpecification objSpec : IsisContext.getSpecificationLoader().allSpecifications()) {
+            for (final ObjectSpecification objSpec : IsisContext.getSpecificationLoader().currentSpecifications()) {
                 final Class<?> correspondingClass = objSpec.getCorrespondingClass();
                 if(correspondingClass.isEnum()) {
                     final Object[] enumConstants = correspondingClass.getEnumConstants();
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
index 08ef740..5dbc65f 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
@@ -82,7 +82,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
         final RepresentationType representationType = RepresentationType.TYPE_LIST;
         init(representationType, Where.ANYWHERE, RepresentationService.Intent.NOT_APPLICABLE);
 
-        final Collection<ObjectSpecification> allSpecifications = getSpecificationLoader().allSpecifications();
+        final Collection<ObjectSpecification> allSpecifications = getSpecificationLoader().currentSpecifications();
 
         final TypeListReprRenderer renderer = new TypeListReprRenderer(getResourceContext(), null, JsonRepresentation.newMap());
         renderer.with(allSpecifications).includesSelf();
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/HomePageReprRenderer.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/HomePageReprRenderer.java
index 174d221..1d048f3 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/HomePageReprRenderer.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/HomePageReprRenderer.java
@@ -64,7 +64,7 @@ public class HomePageReprRenderer extends ReprRendererAbstract<HomePageReprRende
         addLinkToMenuBars();
         addLinkToServices(metaModelContext.streamServiceAdapters());
         addLinkToVersion();
-        addLinkToDomainTypes(((ResourceContext)getRendererContext()).getSpecificationLoader().allSpecifications());
+        addLinkToDomainTypes(((ResourceContext)getRendererContext()).getSpecificationLoader().currentSpecifications());
 
         // inks and extensions
         representation.mapPut("extensions", JsonRepresentation.newMap());
diff --git a/example/application/springapp/src/main/java/isis/incubator/command/IncubatingSpecificationLoader.java b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingSpecificationLoader.java
index d477181..242832b 100644
--- a/example/application/springapp/src/main/java/isis/incubator/command/IncubatingSpecificationLoader.java
+++ b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingSpecificationLoader.java
@@ -24,7 +24,7 @@ public class IncubatingSpecificationLoader implements SpecificationLoader {
 	}
 
 	@Override
-	public List<ObjectSpecification> allSpecifications() {
+	public List<ObjectSpecification> currentSpecifications() {
 		// TODO Auto-generated method stub
 		return null;
 	}
@@ -47,10 +47,4 @@ public class IncubatingSpecificationLoader implements SpecificationLoader {
 		return null;
 	}
 
-	@Override
-	public ObjectSpecification loadSpecification(ObjectSpecId objectSpecId, IntrospectionState upTo) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
 }