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/10/03 10:49:40 UTC

[isis] branch v2 updated: ISIS-2158: spec-loading: fixes another concurrent modification issue

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

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


The following commit(s) were added to refs/heads/v2 by this push:
     new 24438df  ISIS-2158: spec-loading: fixes another concurrent modification issue
24438df is described below

commit 24438dfeb853a0ceef04e00a3aec4d07210053b0
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Oct 3 12:49:33 2019 +0200

    ISIS-2158: spec-loading: fixes another concurrent modification issue
---
 .../isis/commons/internal/collections/_Lists.java  | 10 ++--
 .../commons/internal/collections/_Multimaps.java   | 53 +++++++++++++++++++---
 .../isis/metamodel/facets/CollectionUtils.java     |  2 +-
 .../specimpl/ObjectSpecificationAbstract.java      | 17 ++-----
 4 files changed, 57 insertions(+), 25 deletions(-)

diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
index 7e1f99f..8f734ac 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
@@ -150,19 +150,19 @@ public final class _Lists {
 
     // -- COPY ON WRITE LIST
 
-    public static <T> CopyOnWriteArrayList<T> newCopyOnWriteArrayList() {
+    public static <T> CopyOnWriteArrayList<T> newConcurrentList() {
         return new CopyOnWriteArrayList<T>();
     }
 
-    public static <T> CopyOnWriteArrayList<T> newCopyOnWriteArrayList(@Nullable Collection<T> collection) {
+    public static <T> CopyOnWriteArrayList<T> newConcurrentList(@Nullable Collection<T> collection) {
         if(collection==null) {
-            return newCopyOnWriteArrayList();
+            return newConcurrentList();
         }
         return new CopyOnWriteArrayList<T>(collection);
     }
 
-    public static <T> CopyOnWriteArrayList<T> newCopyOnWriteArrayList(@Nullable Iterable<T> iterable) {
-        return _Collections.collectFromIterable(iterable, _Lists::newCopyOnWriteArrayList,
+    public static <T> CopyOnWriteArrayList<T> newConcurrentList(@Nullable Iterable<T> iterable) {
+        return _Collections.collectFromIterable(iterable, _Lists::newConcurrentList,
                 ()->Collectors.<T, CopyOnWriteArrayList<T>>toCollection(CopyOnWriteArrayList::new) );
     }
 
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Multimaps.java b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Multimaps.java
index 464df9a..94681af 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Multimaps.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Multimaps.java
@@ -32,6 +32,7 @@ import java.util.SortedSet;
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Supplier;
 
 import javax.annotation.Nullable;
@@ -40,6 +41,8 @@ import org.apache.isis.commons.internal.base._Casts;
 
 import static org.apache.isis.commons.internal.base._With.requires;
 
+import lombok.val;
+
 /**
  * <h1>- internal use only -</h1>
  * <p>
@@ -67,6 +70,8 @@ public class _Multimaps {
          * @param value
          */
         public void putElement(K key, V value);
+        
+        public List<V> getOrElseNew(K key);
     }
 
     /**
@@ -82,6 +87,8 @@ public class _Multimaps {
          * @param value
          */
         public void putElement(K key, V value);
+        
+        public Set<V> getOrElseNew(K key);
     }
 
     /**
@@ -108,6 +115,8 @@ public class _Multimaps {
          * @return null if no such element exists
          */
         public V getElement(K1 key, K2 subkey);
+        
+        public Map<K2, V> getOrElseNew(K1 key);
     }
 
     public static <K, V> ListMultimap<K, V> newListMultimap(
@@ -135,8 +144,13 @@ public class _Multimaps {
 
             @Override
             public void putElement(K key, V value) {
-                final Collection<V> collection = delegate.computeIfAbsent(key, __->elementCollectionFactory.get());
-                collection.add(value);
+                getOrElseNew(key).add(value);
+            }
+            
+            @Override
+            public List<V> getOrElseNew(K key) {
+                val collection = delegate.computeIfAbsent(key, __->elementCollectionFactory.get());
+                return collection;
             }
 
         };
@@ -167,8 +181,13 @@ public class _Multimaps {
 
             @Override
             public void putElement(K key, V value) {
-                final Collection<V> collection = delegate.computeIfAbsent(key, __->elementCollectionFactory.get());
-                collection.add(value);
+                getOrElseNew(key).add(value);
+            }
+            
+            @Override
+            public Set<V> getOrElseNew(K key) {
+                val collection = delegate.computeIfAbsent(key, __->elementCollectionFactory.get());
+                return collection;
             }
 
         };
@@ -199,8 +218,7 @@ public class _Multimaps {
 
             @Override
             public void putElement(K1 key, K2 subkey, V value) {
-                final Map<K2, V> elementMap = delegate.computeIfAbsent(key, __->elementMapFactory.get());
-                elementMap.put(subkey, value);
+                getOrElseNew(key).put(subkey, value);
             }
 
             @Override
@@ -208,6 +226,12 @@ public class _Multimaps {
                 final Map<K2, V> elementMap = delegate.get(key);
                 return elementMap!=null ? elementMap.get(subkey) : null;
             }
+            
+            @Override
+            public Map<K2, V> getOrElseNew(K1 key) {
+                val elementMap = delegate.computeIfAbsent(key, __->elementMapFactory.get());
+                return elementMap;
+            }
 
         };
     }
@@ -220,6 +244,13 @@ public class _Multimaps {
     public static <K, V> ListMultimap<K, V> newListMultimap(){
         return newListMultimap(HashMap<K, List<V>>::new, ArrayList::new);
     }
+    
+    /**
+     * @return ConcurrentHashMap of CopyOnWriteArrayList (fully concurrent)
+     */
+    public static <K, V> ListMultimap<K, V> newConcurrentListMultimap(){
+        return newListMultimap(ConcurrentHashMap<K, List<V>>::new, CopyOnWriteArrayList::new);
+    }
 
     /**
      * @return HashMap of HashSets
@@ -227,6 +258,14 @@ public class _Multimaps {
     public static <K, V> SetMultimap<K, V> newSetMultimap(){
         return newSetMultimap(HashMap<K, Set<V>>::new, HashSet::new);
     }
+    
+    /**
+     * @return ConcurrentHashMap of ConcurrentHashMap-key-sets (fully concurrent)
+     */
+    public static <K, V> SetMultimap<K, V> newConcurrentSetMultimap(){
+        return newSetMultimap(ConcurrentHashMap<K, Set<V>>::new, _Sets::newConcurrentHashSet);
+    }
+
 
     /**
      * @param keyComparator - if {@code null} uses natural ordering
@@ -250,7 +289,7 @@ public class _Multimaps {
     }
 
     /**
-     * @return ConcurrentHashMap of HashMaps
+     * @return ConcurrentHashMap of HashMaps (not fully concurrent)
      */
     public static <K1, K2, V> MapMultimap<K1, K2, V> newConcurrentMapMultimap(){
         return newMapMultimap(ConcurrentHashMap<K1, Map<K2, V>>::new, HashMap::new);
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/CollectionUtils.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/CollectionUtils.java
index a3186dc..5e985e7 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/CollectionUtils.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/CollectionUtils.java
@@ -66,7 +66,7 @@ public final class CollectionUtils {
     private final static Map<Class<?>, Function<Iterable<Object>, Object>> factoriesByType = _With.hashMap(
             map-> {
                 // specific list implementations
-                map.put(CopyOnWriteArrayList.class, _Lists::newCopyOnWriteArrayList);
+                map.put(CopyOnWriteArrayList.class, _Lists::newConcurrentList);
                 map.put(LinkedList.class, _Lists::newLinkedList);
                 map.put(ArrayList.class, _Lists::newArrayList);
                 map.put(AbstractList.class, _Lists::newArrayList);
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index f16ba31..9c07cf6 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -23,7 +23,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -38,7 +37,8 @@ import org.apache.isis.commons.exceptions.UnknownTypeException;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.commons.internal.collections._Maps;
+import org.apache.isis.commons.internal.collections._Multimaps;
+import org.apache.isis.commons.internal.collections._Multimaps.ListMultimap;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.commons.internal.ioc.BeanAdapter;
 import org.apache.isis.commons.internal.ioc.BeanSort;
@@ -159,15 +159,8 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
     private final List<ObjectAction> objectActions = _Lists.newArrayList();
 
     // partitions and caches objectActions by type; updated in sortCacheAndUpdateActions()
-    private final Map<ActionType, List<ObjectAction>> objectActionsByType = createObjectActionsByType();
-
-    private static Map<ActionType, List<ObjectAction>> createObjectActionsByType() {
-        final Map<ActionType, List<ObjectAction>> map = _Maps.newHashMap();
-        for (final ActionType type : ActionType.values()) {
-            map.put(type, _Lists.<ObjectAction>newArrayList());
-        }
-        return map;
-    }
+    private final ListMultimap<ActionType, ObjectAction> objectActionsByType = 
+            _Multimaps.newConcurrentListMultimap();
 
     private final List<ObjectSpecification> interfaces = _Lists.newArrayList();
     private final Subclasses directSubclasses = new Subclasses();
@@ -376,7 +369,7 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
             this.objectActions.addAll(orderedActions);
 
             for (final ActionType type : ActionType.values()) {
-                final List<ObjectAction> objectActionForType = objectActionsByType.get(type);
+                val objectActionForType = objectActionsByType.getOrElseNew(type);
                 objectActionForType.clear();
                 objectActions.stream()
                 .filter(ObjectAction.Predicates.ofType(type))