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 14:54:36 UTC
[isis] branch v2 updated: ISIS-2158: spec-loading: another
concurrency issue detected and fixed
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 6aa8df2 ISIS-2158: spec-loading: another concurrency issue detected and fixed
6aa8df2 is described below
commit 6aa8df2750aaaf8bbee5b08f8db450d6c176e084
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Oct 3 16:54:25 2019 +0200
ISIS-2158: spec-loading: another concurrency issue detected and fixed
---
.../commons/internal/collections/_Multimaps.java | 56 ++++
.../apache/isis/metamodel/facets/FacetFactory.java | 1 +
.../metamodel/facets/MethodRemoverConstants.java | 2 +-
.../metamodel/specloader/SpecificationLoader.java | 1 -
.../specloader/facetprocessor/FacetProcessor.java | 292 ++++++++++-----------
.../specimpl/ObjectSpecificationAbstract.java | 67 +++--
6 files changed, 243 insertions(+), 176 deletions(-)
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 94681af..475f9e2 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
@@ -21,6 +21,7 @@ package org.apache.isis.commons.internal.collections;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@@ -71,7 +72,26 @@ public class _Multimaps {
*/
public void putElement(K key, V value);
+ /**
+ * Returns the List to which the specified key is mapped,
+ * or a new List if this map contains no mapping for the key.
+ * <p> In the latter case the List is also put onto this map.
+ * @param key
+ * @return
+ */
public List<V> getOrElseNew(K key);
+
+ /**
+ * Returns the List to which the specified key is mapped,
+ * or an immutable empty List if this map contains no mapping for the key.
+ * <p> In the latter case the List is not put onto this map.
+ * @param key
+ * @return
+ */
+ default public List<V> getOrElseEmpty(K key) {
+ return getOrDefault(key, Collections.emptyList());
+ }
+
}
/**
@@ -88,7 +108,25 @@ public class _Multimaps {
*/
public void putElement(K key, V value);
+ /**
+ * Returns the Set to which the specified key is mapped,
+ * or a new Set if this map contains no mapping for the key.
+ * <p> In the latter case the Set is also put onto this map.
+ * @param key
+ * @return
+ */
public Set<V> getOrElseNew(K key);
+
+ /**
+ * Returns the Set to which the specified key is mapped,
+ * or an immutable empty Set if this map contains no mapping for the key.
+ * <p> In the latter case the Set is not put onto this map.
+ * @param key
+ * @return
+ */
+ default public Set<V> getOrElseEmpty(K key) {
+ return getOrDefault(key, Collections.emptySet());
+ }
}
/**
@@ -116,7 +154,25 @@ public class _Multimaps {
*/
public V getElement(K1 key, K2 subkey);
+ /**
+ * Returns the Map to which the specified key is mapped,
+ * or a new Map if this map contains no mapping for the key.
+ * <p> In the latter case the Map is also put onto this map.
+ * @param key
+ * @return
+ */
public Map<K2, V> getOrElseNew(K1 key);
+
+ /**
+ * Returns the Map to which the specified key is mapped,
+ * or an immutable empty Map if this map contains no mapping for the key.
+ * <p> In the latter case the Map not is put onto this map.
+ * @param key
+ * @return
+ */
+ default public Map<K2, V> getOrElseEmpty(K1 key) {
+ return getOrDefault(key, Collections.emptyMap());
+ }
}
public static <K, V> ListMultimap<K, V> newListMultimap(
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/FacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/FacetFactory.java
index c08f5ea..63dbe9f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/FacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/FacetFactory.java
@@ -117,6 +117,7 @@ public interface FacetFactory {
* Used by the Java5 Reflector's <tt>ProgrammingModel</tt> to reduce the
* number of {@link FacetFactory factory}s that are queried when building up
* the meta-model.
+ *
*/
List<FeatureType> getFeatureTypes();
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/MethodRemoverConstants.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/MethodRemoverConstants.java
index ecac2c4..254ce21 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/MethodRemoverConstants.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/MethodRemoverConstants.java
@@ -27,7 +27,7 @@ import org.apache.isis.metamodel.methodutils.MethodScope;
public class MethodRemoverConstants {
- public static MethodRemover NULL = new MethodRemover() {
+ public static MethodRemover NOOP = new MethodRemover() {
@Override
public void removeMethods(final MethodScope methodScope, final String prefix, final Class<?> returnType, final boolean canBeVoid, final int paramCount, Consumer<Method> onRemoval) {
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java
index 9534e44..d19663a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java
@@ -19,7 +19,6 @@
package org.apache.isis.metamodel.specloader;
import java.util.Collection;
-import java.util.List;
import javax.annotation.Nullable;
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/facetprocessor/FacetProcessor.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/facetprocessor/FacetProcessor.java
index 9d0e5ac..28a04d9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/facetprocessor/FacetProcessor.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/facetprocessor/FacetProcessor.java
@@ -27,8 +27,11 @@ import java.util.Map;
import java.util.Set;
import org.apache.isis.applib.services.inject.ServiceInjector;
+import org.apache.isis.commons.internal.base._Lazy;
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.metamodel.MetaModelContext;
import org.apache.isis.metamodel.commons.ListExtensions;
import org.apache.isis.metamodel.facetapi.FacetHolder;
@@ -78,10 +81,9 @@ public class FacetProcessor {
* Derived from factories that implement
* {@link MethodPrefixBasedFacetFactory}.
*
- * <p>
- * If <tt>null</tt>, indicates that the cache hasn't been built.
*/
- private List<String> cachedMethodPrefixes;
+ private final _Lazy<List<String>> methodPrefixes =
+ _Lazy.threadSafe(this::init_methodPrefixes);
/**
* All registered {@link FacetFactory factories} that implement
@@ -90,19 +92,16 @@ public class FacetProcessor {
* <p>
* Used within {@link #recognizes(Method)}.
*
- * <p>
- * If <tt>null</tt>, indicates that the cache hasn't been built.
*/
- private List<MethodFilteringFacetFactory> cachedMethodFilteringFactories;
+ private final _Lazy<List<MethodFilteringFacetFactory>> methodFilteringFactories =
+ _Lazy.threadSafe(this::init_methodFilteringFactories);
/**
* All registered {@link FacetFactory factories} that implement
* {@link ContributeeMemberFacetFactory}.
- *
- * <p>
- * If <tt>null</tt>, indicates that the cache hasn't been built.
*/
- private List<ContributeeMemberFacetFactory> cachedContributeeMemberFacetFactories;
+ private final _Lazy<List<ContributeeMemberFacetFactory>> contributeeMemberFacetFactories =
+ _Lazy.threadSafe(this::init_contributeeMemberFacetFactories);
/**
* All registered {@link FacetFactory factories} that implement
@@ -110,11 +109,9 @@ public class FacetProcessor {
*
* <p>
* Used within {@link #recognizes(Method)}.
- *
- * <p>
- * If <tt>null</tt>, indicates that the cache hasn't been built.
*/
- private List<PropertyOrCollectionIdentifyingFacetFactory> cachedPropertyOrCollectionIdentifyingFactories;
+ private final _Lazy<List<PropertyOrCollectionIdentifyingFacetFactory>> propertyOrCollectionIdentifyingFactories =
+ _Lazy.threadSafe(this::init_propertyOrCollectionIdentifyingFactories);
/**
* ObjectFeatureType => List<FacetFactory>
@@ -123,26 +120,33 @@ public class FacetProcessor {
* Lazily initialized, then cached. The lists remain in the same order that
* the factories were {@link #registerFactory(FacetFactory) registered}.
*/
- private Map<FeatureType, List<FacetFactory>> factoryListByFeatureType = null;
-
- // //////////////////////////////////////////////////
- // init, shutdown (application scoped)
- // //////////////////////////////////////////////////
-
+ private final _Lazy<ListMultimap<FeatureType, FacetFactory>> factoryListByFeatureType =
+ _Lazy.threadSafe(this::init_factoriesByFeatureType);
+
+ // -- LIFECYCLE
+
public void init() {
+ cleanUp();
+
serviceInjector = MetaModelContext.current().getServiceInjector();
val facetFactoryList = programmingModel.getList();
facetFactoryList.forEach(this::registerFactory);
}
public void shutdown() {
+ cleanUp();
}
- public void registerFactory(final FacetFactory factory) {
+ private void cleanUp() {
clearCaches();
+ factories.clear();
+ serviceInjector = null;
+ factoryByFactoryType.clear();
+ }
+
+ private void registerFactory(FacetFactory factory) {
factoryByFactoryType.put(factory.getClass(), factory);
factories.add(factory);
-
injectDependenciesInto(factory);
}
@@ -150,7 +154,7 @@ public class FacetProcessor {
* This is <tt>public</tt> so that can be used for <tt>@Facets</tt>
* processing.
*/
- public void injectDependenciesInto(final FacetFactory factory) {
+ public void injectDependenciesInto(FacetFactory factory) {
serviceInjector.injectServicesInto(factory);
}
@@ -163,15 +167,16 @@ public class FacetProcessor {
* {@link PropertyOrCollectionIdentifyingFacetFactory}s.
*/
public Set<Method> findAssociationCandidateAccessors(
- final Collection<Method> methods,
- final Set<Method> candidates) {
+ Collection<Method> methods,
+ Set<Method> candidates) {
+
+ val factories = propertyOrCollectionIdentifyingFactories.get();
- cachePropertyOrCollectionIdentifyingFacetFactoriesIfRequired();
for (val method : methods) {
if (method == null) {
continue;
}
- for (val facetFactory : cachedPropertyOrCollectionIdentifyingFactories) {
+ for (val facetFactory : factories) {
if (facetFactory.isPropertyOrCollectionAccessorCandidate(method)) {
candidates.add(method);
}
@@ -188,9 +193,11 @@ public class FacetProcessor {
* <p>
* Intended to be called after {@link #findAndRemovePropertyAccessors(org.apache.isis.metamodel.facetapi.MethodRemover, java.util.List)} once only reference properties remain.
*/
- public void findAndRemovePropertyAccessors(final MethodRemover methodRemover, final List<Method> methodListToAppendTo) {
- cachePropertyOrCollectionIdentifyingFacetFactoriesIfRequired();
- for (final PropertyOrCollectionIdentifyingFacetFactory facetFactory : cachedPropertyOrCollectionIdentifyingFactories) {
+ public void findAndRemovePropertyAccessors(
+ MethodRemover methodRemover,
+ List<Method> methodListToAppendTo) {
+
+ for (val facetFactory : propertyOrCollectionIdentifyingFactories.get()) {
facetFactory.findAndRemovePropertyAccessors(methodRemover, methodListToAppendTo);
}
}
@@ -203,9 +210,11 @@ public class FacetProcessor {
* @see PropertyOrCollectionIdentifyingFacetFactory#findAndRemoveCollectionAccessors(MethodRemover,
* List)
*/
- public void findAndRemoveCollectionAccessors(final MethodRemover methodRemover, final List<Method> methodListToAppendTo) {
- cachePropertyOrCollectionIdentifyingFacetFactoriesIfRequired();
- for (final PropertyOrCollectionIdentifyingFacetFactory facetFactory : cachedPropertyOrCollectionIdentifyingFactories) {
+ public void findAndRemoveCollectionAccessors(
+ MethodRemover methodRemover,
+ List<Method> methodListToAppendTo) {
+
+ for (val facetFactory : propertyOrCollectionIdentifyingFactories.get()) {
facetFactory.findAndRemoveCollectionAccessors(methodRemover, methodListToAppendTo);
}
}
@@ -228,17 +237,15 @@ public class FacetProcessor {
* factory set does the work) is a slight performance optimization for when
* there are multiple facet factories that search for the same prefix.
*/
- public boolean recognizes(final Method method) {
- cacheMethodPrefixesIfRequired();
- final String methodName = method.getName();
- for (final String prefix : cachedMethodPrefixes) {
+ public boolean recognizes(Method method) {
+ val methodName = method.getName();
+ for (val prefix : methodPrefixes.get()) {
if (methodName.startsWith(prefix)) {
return true;
}
}
- cacheMethodFilteringFacetFactoriesIfRequired();
- for (final MethodFilteringFacetFactory factory : cachedMethodFilteringFactories) {
+ for (val factory : methodFilteringFactories.get()) {
if (factory.recognizes(method)) {
return true;
}
@@ -247,9 +254,9 @@ public class FacetProcessor {
return false;
}
- public void processObjectSpecId(final Class<?> cls, final FacetHolder facetHolder) {
- final List<ObjectSpecIdFacetFactory> factoryList = getObjectSpecIfFacetFactoryList();
- for (final ObjectSpecIdFacetFactory facetFactory : factoryList) {
+ public void processObjectSpecId(Class<?> cls, FacetHolder facetHolder) {
+ val factoryList = getObjectSpecIfFacetFactoryList();
+ for (val facetFactory : factoryList) {
facetFactory.process(new ProcessObjectSpecIdContext(cls, facetHolder));
}
}
@@ -259,14 +266,16 @@ public class FacetProcessor {
private List<ObjectSpecIdFacetFactory> getObjectSpecIfFacetFactoryList() {
if(objectSpecIfFacetFactoryList == null) {
- List<ObjectSpecIdFacetFactory> facetFactories = _Lists.newArrayList();
- final List<FacetFactory> factoryList = getFactoryListByFeatureType(FeatureType.OBJECT);
- for (final FacetFactory facetFactory : factoryList) {
+ val facetFactories = _Lists.<ObjectSpecIdFacetFactory>newArrayList();
+
+ factoryListByFeatureType.get().getOrElseEmpty(FeatureType.OBJECT)
+ .forEach(facetFactory->{
if (facetFactory instanceof ObjectSpecIdFacetFactory) {
- final ObjectSpecIdFacetFactory objectSpecIdFacetFactory = (ObjectSpecIdFacetFactory) facetFactory;
+ val objectSpecIdFacetFactory = (ObjectSpecIdFacetFactory) facetFactory;
facetFactories.add(objectSpecIdFacetFactory);
}
- }
+ });
+
objectSpecIfFacetFactoryList = Collections.unmodifiableList(facetFactories);
}
return objectSpecIfFacetFactoryList;
@@ -288,13 +297,17 @@ public class FacetProcessor {
* - holder to attach facets to.
*/
public void process(
- final Class<?> cls,
- final MethodRemover methodRemover,
- final FacetHolder facetHolder) {
- final List<FacetFactory> factoryList = getFactoryListByFeatureType(FeatureType.OBJECT);
- for (final FacetFactory facetFactory : factoryList) {
- facetFactory.process(new ProcessClassContext(cls, removerElseNullRemover(methodRemover), facetHolder));
- }
+ Class<?> cls,
+ MethodRemover methodRemover,
+ FacetHolder facetHolder) {
+
+ val ctx = new ProcessClassContext(
+ cls,
+ removerElseNoopRemover(methodRemover),
+ facetHolder);
+
+ factoryListByFeatureType.get().getOrElseEmpty(FeatureType.OBJECT)
+ .forEach(facetFactory->facetFactory.process(ctx));
}
@@ -319,28 +332,28 @@ public class FacetProcessor {
* action, collection etc)
*/
public void process(
- final Class<?> cls,
- final Method method,
- final MethodRemover methodRemover,
- final FacetedMethod facetedMethod,
- final FeatureType featureType) {
- final List<FacetFactory> factoryList = getFactoryListByFeatureType(featureType);
- final ProcessMethodContext processMethodContext =
- new ProcessMethodContext(cls, featureType, method, removerElseNullRemover(methodRemover), facetedMethod);
- for (final FacetFactory facetFactory : factoryList) {
- facetFactory.process(processMethodContext);
- }
+ Class<?> cls,
+ Method method,
+ MethodRemover methodRemover,
+ FacetedMethod facetedMethod,
+ FeatureType featureType) {
+
+
+ val processMethodContext =
+ new ProcessMethodContext(cls, featureType, method, removerElseNoopRemover(methodRemover), facetedMethod);
+
+ factoryListByFeatureType.get().getOrElseEmpty(featureType)
+ .forEach(facetFactory->facetFactory.process(processMethodContext));
}
- public void processMemberOrder(
- final ObjectMember facetHolder) {
- cacheContributeeMemberFacetFactoriesIfRequired();
- final ContributeeMemberFacetFactory.ProcessContributeeMemberContext processMemberContext =
+ public void processMemberOrder(ObjectMember facetHolder) {
+
+ val processMemberContext =
new ContributeeMemberFacetFactory.ProcessContributeeMemberContext(facetHolder);
- for (final ContributeeMemberFacetFactory facetFactory : cachedContributeeMemberFacetFactories) {
- facetFactory.process(processMemberContext);
- }
+ contributeeMemberFacetFactories.get().forEach(facetFactory->
+ facetFactory.process(processMemberContext));
+
}
/**
@@ -362,121 +375,100 @@ public class FacetProcessor {
* @param facetedMethodParameter
*/
public void processParams(
- final Class<?> introspectedClass,
- final Method method,
- final int paramNum,
- final MethodRemover methodRemover,
- final FacetedMethodParameter facetedMethodParameter) {
- for (FeatureType featureType : FeatureType.PARAMETERS_ONLY) {
+ Class<?> introspectedClass,
+ Method method,
+ int paramNum,
+ MethodRemover methodRemover,
+ FacetedMethodParameter facetedMethodParameter) {
+
+ for (val featureType : FeatureType.PARAMETERS_ONLY) {
processParams(introspectedClass, method, paramNum, methodRemover, facetedMethodParameter, featureType);
}
}
public void processParams(
- final Class<?> introspectedClass,
- final Method method,
- final int paramNum,
- final MethodRemover methodRemover,
- final FacetedMethodParameter facetedMethodParameter,
- final FeatureType featureType) {
- final List<FacetFactory> factoryList = getFactoryListByFeatureType(featureType);
- final ProcessParameterContext processParameterContext =
+ Class<?> introspectedClass,
+ Method method,
+ int paramNum,
+ MethodRemover methodRemover,
+ FacetedMethodParameter facetedMethodParameter,
+ FeatureType featureType) {
+
+ val processParameterContext =
new ProcessParameterContext(introspectedClass, method, paramNum, methodRemover, facetedMethodParameter);
- for (final FacetFactory facetFactory : factoryList) {
- facetFactory.processParams(processParameterContext);
- }
+
+ factoryListByFeatureType.get().getOrElseEmpty(featureType)
+ .forEach(facetFactory->facetFactory.processParams(processParameterContext));
}
- private List<FacetFactory> getFactoryListByFeatureType(final FeatureType featureType) {
- cacheByFeatureTypeIfRequired();
- List<FacetFactory> list = factoryListByFeatureType.get(featureType);
- return list != null? list: Collections.<FacetFactory>emptyList();
- }
private void clearCaches() {
- factoryListByFeatureType = null;
- cachedMethodPrefixes = null;
- cachedMethodFilteringFactories = null;
- cachedPropertyOrCollectionIdentifyingFactories = null;
+ factoryListByFeatureType.clear();
+ methodPrefixes.clear();
+ methodFilteringFactories.clear();
+ contributeeMemberFacetFactories.clear();
+ propertyOrCollectionIdentifyingFactories.clear();
}
+
+ // -- INITIALIZERS
- private synchronized void cacheByFeatureTypeIfRequired() {
- if (factoryListByFeatureType != null) {
- return;
- }
- factoryListByFeatureType = _Maps.newHashMap();
- for (final FacetFactory factory : factories) {
- final List<FeatureType> featureTypes = factory.getFeatureTypes();
- for (final FeatureType featureType : featureTypes) {
- final List<FacetFactory> factoryList = getList(factoryListByFeatureType, featureType);
- factoryList.add(factory);
- }
+ private ListMultimap<FeatureType, FacetFactory> init_factoriesByFeatureType() {
+ val factoryListByFeatureType = _Multimaps.<FeatureType, FacetFactory>newListMultimap();
+ for (val factory : factories) {
+ factory.getFeatureTypes().forEach(featureType->
+ factoryListByFeatureType.putElement(featureType, factory));
}
+ return factoryListByFeatureType;
}
- private synchronized void cacheMethodPrefixesIfRequired() {
- if (cachedMethodPrefixes != null) {
- return;
- }
- cachedMethodPrefixes = _Lists.newArrayList();
- for (final FacetFactory facetFactory : factories) {
+ private List<String> init_methodPrefixes() {
+ val cachedMethodPrefixes = _Lists.<String>newArrayList();
+ for (val facetFactory : factories) {
if (facetFactory instanceof MethodPrefixBasedFacetFactory) {
- final MethodPrefixBasedFacetFactory methodPrefixBasedFacetFactory = (MethodPrefixBasedFacetFactory) facetFactory;
+ val methodPrefixBasedFacetFactory = (MethodPrefixBasedFacetFactory) facetFactory;
ListExtensions.mergeWith(cachedMethodPrefixes, methodPrefixBasedFacetFactory.getPrefixes());
}
}
+ return cachedMethodPrefixes;
}
- private synchronized void cacheMethodFilteringFacetFactoriesIfRequired() {
- if (cachedMethodFilteringFactories != null) {
- return;
- }
- cachedMethodFilteringFactories = _Lists.newArrayList();
- for (final FacetFactory factory : factories) {
+ private List<MethodFilteringFacetFactory> init_methodFilteringFactories() {
+ val methodFilteringFactories = _Lists.<MethodFilteringFacetFactory>newArrayList();
+ for (val factory : factories) {
if (factory instanceof MethodFilteringFacetFactory) {
- final MethodFilteringFacetFactory methodFilteringFacetFactory = (MethodFilteringFacetFactory) factory;
- cachedMethodFilteringFactories.add(methodFilteringFacetFactory);
+ val methodFilteringFacetFactory = (MethodFilteringFacetFactory) factory;
+ methodFilteringFactories.add(methodFilteringFacetFactory);
}
}
+ return methodFilteringFactories;
}
- private synchronized void cacheContributeeMemberFacetFactoriesIfRequired() {
- if (cachedContributeeMemberFacetFactories != null) {
- return;
- }
- cachedContributeeMemberFacetFactories = _Lists.newArrayList();
- for (final FacetFactory factory : factories) {
+ private List<ContributeeMemberFacetFactory> init_contributeeMemberFacetFactories() {
+ val contributeeMemberFacetFactories = _Lists.<ContributeeMemberFacetFactory>newArrayList();
+ for (val factory : factories) {
if (factory instanceof ContributeeMemberFacetFactory) {
final ContributeeMemberFacetFactory memberOrderingFacetFactory = (ContributeeMemberFacetFactory) factory;
- cachedContributeeMemberFacetFactories.add(memberOrderingFacetFactory);
+ contributeeMemberFacetFactories.add(memberOrderingFacetFactory);
}
}
+ return contributeeMemberFacetFactories;
}
- private synchronized void cachePropertyOrCollectionIdentifyingFacetFactoriesIfRequired() {
- if (cachedPropertyOrCollectionIdentifyingFactories != null) {
- return;
- }
- cachedPropertyOrCollectionIdentifyingFactories = _Lists.newArrayList();
- for (FacetFactory factory : factories) {
+ private List<PropertyOrCollectionIdentifyingFacetFactory> init_propertyOrCollectionIdentifyingFactories() {
+ val propertyOrCollectionIdentifyingFactories = _Lists.<PropertyOrCollectionIdentifyingFacetFactory>newArrayList();
+ for (val factory : factories) {
if (factory instanceof PropertyOrCollectionIdentifyingFacetFactory) {
- final PropertyOrCollectionIdentifyingFacetFactory identifyingFacetFactory = (PropertyOrCollectionIdentifyingFacetFactory) factory;
- cachedPropertyOrCollectionIdentifyingFactories.add(identifyingFacetFactory);
+ val identifyingFacetFactory = (PropertyOrCollectionIdentifyingFacetFactory) factory;
+ propertyOrCollectionIdentifyingFactories.add(identifyingFacetFactory);
}
}
+ return propertyOrCollectionIdentifyingFactories;
}
+
+ // -- HELPER
- private static <K, T> List<T> getList(final Map<K, List<T>> map, final K key) {
- List<T> list = map.get(key);
- if (list == null) {
- list = _Lists.newArrayList();
- map.put(key, list);
- }
- return list;
- }
-
- private MethodRemover removerElseNullRemover(final MethodRemover methodRemover) {
- return methodRemover != null ? methodRemover : MethodRemoverConstants.NULL;
+ private static MethodRemover removerElseNoopRemover(MethodRemover methodRemover) {
+ return methodRemover != null ? methodRemover : MethodRemoverConstants.NOOP;
}
}
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 9c07cf6..f4d397d 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
@@ -34,12 +34,14 @@ import javax.enterprise.inject.Vetoed;
import org.apache.isis.applib.Identifier;
import org.apache.isis.applib.annotation.Where;
import org.apache.isis.commons.exceptions.UnknownTypeException;
+import org.apache.isis.commons.internal.base._Lazy;
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._Multimaps;
import org.apache.isis.commons.internal.collections._Multimaps.ListMultimap;
import org.apache.isis.commons.internal.collections._Sets;
+import org.apache.isis.commons.internal.collections._Streams;
import org.apache.isis.commons.internal.ioc.BeanAdapter;
import org.apache.isis.commons.internal.ioc.BeanSort;
import org.apache.isis.commons.internal.reflection._Reflect;
@@ -163,6 +165,13 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
_Multimaps.newConcurrentListMultimap();
private final List<ObjectSpecification> interfaces = _Lists.newArrayList();
+
+ // defensive immutable lazy copy of interfaces
+ private final _Lazy<List<ObjectSpecification>> unmodifiableInterfaces =
+ _Lazy.threadSafe(()->Collections.unmodifiableList(new ArrayList<>(interfaces)));
+
+
+
private final Subclasses directSubclasses = new Subclasses();
// built lazily
private Subclasses transitiveSubclasses;
@@ -330,8 +339,11 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
}
protected void updateInterfaces(final List<ObjectSpecification> interfaces) {
- this.interfaces.clear();
- this.interfaces.addAll(interfaces);
+ synchronized(unmodifiableInterfaces) {
+ this.interfaces.clear();
+ this.interfaces.addAll(interfaces);
+ unmodifiableInterfaces.clear();
+ }
}
private void updateAsSubclassTo(final ObjectSpecification supertypeSpec) {
@@ -525,26 +537,30 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
@Override
public <Q extends Facet> Q getFacet(final Class<Q> facetType) {
- // lookup facet holder's facet
- final Stream<Q> facets1 = _NullSafe.streamNullable(super.getFacet(facetType));
-
- // lookup all interfaces
- final Stream<Q> facets2 = _NullSafe.stream(interfaces)
- .filter(_NullSafe::isPresent) // just in case
- .map(interfaceSpec->interfaceSpec.getFacet(facetType));
-
- // search up the inheritance hierarchy
- final Stream<Q> facets3 = _NullSafe.streamNullable(superclass())
- .map(superSpec->superSpec.getFacet(facetType));
-
- final Stream<Q> facetsCombined = Stream.concat(Stream.concat(facets1, facets2), facets3);
-
- final NotANoopFacetFilter<Q> notANoopFacetFilter = new NotANoopFacetFilter<>();
-
- return facetsCombined
- .filter(notANoopFacetFilter)
- .findFirst()
- .orElse(notANoopFacetFilter.noopFacet);
+ synchronized(unmodifiableInterfaces) {
+
+ // lookup facet holder's facet
+ val facets1 = _NullSafe.streamNullable(super.getFacet(facetType));
+
+ // lookup all interfaces
+ val facets2 = _NullSafe.stream(interfaces())
+ .filter(_NullSafe::isPresent) // just in case
+ .map(interfaceSpec->interfaceSpec.getFacet(facetType));
+
+ // search up the inheritance hierarchy
+ val facets3 = _NullSafe.streamNullable(superclass())
+ .map(superSpec->superSpec.getFacet(facetType));
+
+ val facetsCombined = _Streams.concat(facets1, facets2, facets3);
+
+ val notANoopFacetFilter = new NotANoopFacetFilter<Q>();
+
+ return facetsCombined
+ .filter(notANoopFacetFilter)
+ .findFirst()
+ .orElse(notANoopFacetFilter.noopFacet);
+
+ }
}
@Vetoed
@@ -608,7 +624,7 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
@Override
public Collection<ObjectSpecification> interfaces() {
- return Collections.unmodifiableList(interfaces);
+ return unmodifiableInterfaces.get();
}
@Override
@@ -1019,7 +1035,10 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
final Object servicePojo,
final List<ObjectAction> contributeeActionsToAppendTo) {
- log.debug("{} : addContributeeActionsIfAny(...); servicePojo class is: {}", this.getFullIdentifier(), servicePojo.getClass().getName());
+ if(log.isDebugEnabled()) {
+ log.debug("{} : addContributeeActionsIfAny(...); servicePojo class is: {}",
+ this.getFullIdentifier(), servicePojo.getClass().getName());
+ }
final Class<?> serviceType = servicePojo.getClass();
final ObjectSpecification specification = getSpecificationLoader().loadSpecification(serviceType,