This is an automated email from the ASF dual-hosted git repository. rmannibucau pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/openwebbeans.git
The following commit(s) were added to refs/heads/master by this push: new cd25746 [OWB-1357] skip extension events resolution when there is no observer for them cd25746 is described below commit cd2574623f7dbfd00c0eb0439bc1042b29b9f52d Author: Romain Manni-Bucau <rmannibu...@gmail.com> AuthorDate: Wed Dec 16 14:33:14 2020 +0100 [OWB-1357] skip extension events resolution when there is no observer for them --- .../org/apache/webbeans/config/BeansDeployer.java | 5 +- .../apache/webbeans/event/NotificationManager.java | 284 ++++++++++++++++++++- .../test/portable/events/PortableEventTest.java | 1 - 3 files changed, 285 insertions(+), 5 deletions(-) diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/config/BeansDeployer.java b/webbeans-impl/src/main/java/org/apache/webbeans/config/BeansDeployer.java index 20489b1..a7b2337 100644 --- a/webbeans-impl/src/main/java/org/apache/webbeans/config/BeansDeployer.java +++ b/webbeans-impl/src/main/java/org/apache/webbeans/config/BeansDeployer.java @@ -309,7 +309,10 @@ public class BeansDeployer // activate InjectionResolver cache now webBeansContext.getBeanManagerImpl().getInjectionResolver().setStartup(false); - + + // drop no more needed memory data + webBeansContext.getNotificationManager().afterStart(); + validateAlternatives(beanAttributesPerBda); validateInjectionPoints(); diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/event/NotificationManager.java b/webbeans-impl/src/main/java/org/apache/webbeans/event/NotificationManager.java index 7c7ae7c..dede48e 100644 --- a/webbeans-impl/src/main/java/org/apache/webbeans/event/NotificationManager.java +++ b/webbeans-impl/src/main/java/org/apache/webbeans/event/NotificationManager.java @@ -33,6 +33,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -45,6 +46,7 @@ import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; import javax.enterprise.context.RequestScoped; import javax.enterprise.event.NotificationOptions; @@ -61,7 +63,17 @@ import javax.enterprise.inject.spi.EventContext; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ObserverMethod; import javax.enterprise.inject.spi.ProcessAnnotatedType; +import javax.enterprise.inject.spi.ProcessBean; +import javax.enterprise.inject.spi.ProcessBeanAttributes; +import javax.enterprise.inject.spi.ProcessInjectionPoint; +import javax.enterprise.inject.spi.ProcessInjectionTarget; +import javax.enterprise.inject.spi.ProcessManagedBean; +import javax.enterprise.inject.spi.ProcessObserverMethod; import javax.enterprise.inject.spi.ProcessProducer; +import javax.enterprise.inject.spi.ProcessProducerField; +import javax.enterprise.inject.spi.ProcessProducerMethod; +import javax.enterprise.inject.spi.ProcessSyntheticBean; +import javax.enterprise.inject.spi.ProcessSyntheticObserverMethod; import org.apache.webbeans.component.AbstractOwbBean; import org.apache.webbeans.config.OWBLogConst; @@ -82,6 +94,9 @@ import org.apache.webbeans.util.ClassUtil; import org.apache.webbeans.util.GenericsUtil; import org.apache.webbeans.util.WebBeansUtil; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toMap; + public final class NotificationManager { private final Map<Type, Set<ObserverMethod<?>>> observers = new ConcurrentHashMap<>(); @@ -112,12 +127,40 @@ public final class NotificationManager } }; + // idea is to be able to skip O(n) events in favor of an algorithm closer to O(1) impl + // statistically, it is not rare to not use all these events so we enable to skip most of them + private Map<Type, Set<ObserverMethod<?>>> processAnnotatedTypeObservers; + private Map<Type, Set<ObserverMethod<?>>> processBeanAttributesObservers; + private Map<Type, Set<ObserverMethod<?>>> processInjectionTargetObservers; + private Map<Type, Set<ObserverMethod<?>>> processManagedBeanObservers; + private Map<Type, Set<ObserverMethod<?>>> processBeanObservers; + private Map<Type, Set<ObserverMethod<?>>> processInjectionPointObservers; + private Map<Type, Set<ObserverMethod<?>>> processObserverMethodObservers; + private Map<Type, Set<ObserverMethod<?>>> processProducerObservers; + private Map<Type, Set<ObserverMethod<?>>> processProducerFieldObservers; + private Map<Type, Set<ObserverMethod<?>>> processProducerMethodObservers; + private Map<Type, Set<ObserverMethod<?>>> processSyntheticBeanObservers; + private Map<Type, Set<ObserverMethod<?>>> processSyntheticObserverMethodObservers; + public NotificationManager(WebBeansContext webBeansContext) { this.webBeansContext = webBeansContext; this.defaultNotificationOptions = NotificationOptions.ofExecutor(getDefaultExecutor()); } + public void afterStart() + { + Stream.of( + processAnnotatedTypeObservers, processBeanAttributesObservers, + processInjectionTargetObservers, processManagedBeanObservers, + processBeanObservers, processInjectionPointObservers, + processObserverMethodObservers, processProducerObservers, + processProducerFieldObservers, processProducerMethodObservers, + processSyntheticBeanObservers, processSyntheticObserverMethodObservers) + .filter(Objects::nonNull) + .forEach(Map::clear); + } + private Executor getDefaultExecutor() { // here it would be nice to support to use a produced bean like @Named("openwebbeansCdiExecutor") @@ -189,6 +232,177 @@ public final class NotificationManager public <T> Collection<ObserverMethod<? super T>> resolveObservers(T event, EventMetadataImpl metadata, boolean isLifecycleEvent) { + if (isLifecycleEvent) // goal here is to skip any resolution if not needed + { + if (event instanceof ProcessAnnotatedType) + { + if (processAnnotatedTypeObservers == null) + { + processAnnotatedTypeObservers = findObservers(ProcessAnnotatedType.class); + } + if (processAnnotatedTypeObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessManagedBean) + { + if (processManagedBeanObservers == null) + { + processManagedBeanObservers = findObservers(ProcessManagedBean.class); + if (processBeanObservers == null) + { + processBeanObservers = findObservers(ProcessBean.class); + } + processBeanObservers.forEach((k, v) -> processManagedBeanObservers + .computeIfAbsent(k, it -> new HashSet<>()) + .addAll(v)); + } + if (processManagedBeanObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessProducerField) + { + if (processProducerFieldObservers == null) + { + processProducerFieldObservers = findObservers(ProcessProducerField.class); + if (processBeanObservers == null) + { + processBeanObservers = findObservers(ProcessBean.class); + } + processBeanObservers.forEach((k, v) -> processProducerFieldObservers + .computeIfAbsent(k, it -> new HashSet<>()) + .addAll(v)); + } + if (processProducerFieldObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessProducerMethod) + { + if (processProducerMethodObservers == null) + { + processProducerMethodObservers = findObservers(ProcessProducerMethod.class); + if (processBeanObservers == null) + { + processBeanObservers = findObservers(ProcessBean.class); + } + processBeanObservers.forEach((k, v) -> processProducerMethodObservers + .computeIfAbsent(k, it -> new HashSet<>()) + .addAll(v)); + } + if (processProducerMethodObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessSyntheticBean) + { + if (processSyntheticBeanObservers == null) + { + processSyntheticBeanObservers = findObservers(ProcessSyntheticBean.class); + if (processBeanObservers == null) + { + processBeanObservers = findObservers(ProcessBean.class); + } + processBeanObservers.forEach((k, v) -> processSyntheticBeanObservers + .computeIfAbsent(k, it -> new HashSet<>()) + .addAll(v)); + } + if (processSyntheticBeanObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessSyntheticObserverMethod) + { + if (processSyntheticObserverMethodObservers == null) + { + processSyntheticObserverMethodObservers = findObservers(ProcessSyntheticObserverMethod.class); + if (processObserverMethodObservers == null) + { + processObserverMethodObservers = findObservers(ProcessObserverMethod.class); + } + processObserverMethodObservers.forEach((k, v) -> processSyntheticObserverMethodObservers + .computeIfAbsent(k, it -> new HashSet<>()) + .addAll(v)); + } + if (processSyntheticObserverMethodObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessBean) + { + if (processBeanObservers == null) + { + processBeanObservers = findObservers(ProcessBean.class); + } + if (processBeanObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessBeanAttributes) + { + if (processBeanAttributesObservers == null) + { + processBeanAttributesObservers = findObservers(ProcessBeanAttributes.class); + } + if (processBeanAttributesObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessInjectionTarget) + { + if (processInjectionTargetObservers == null) + { + processInjectionTargetObservers = findObservers(ProcessInjectionTarget.class); + } + if (processInjectionTargetObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessInjectionPoint) + { + if (processInjectionPointObservers == null) + { + processInjectionPointObservers = findObservers(ProcessInjectionPoint.class); + } + if (processInjectionPointObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessObserverMethod) + { + if (processObserverMethodObservers == null) + { + processObserverMethodObservers = findObservers(ProcessObserverMethod.class); + } + if (processObserverMethodObservers.isEmpty()) + { + return emptyList(); + } + } + else if (event instanceof ProcessProducer) + { + if (processProducerObservers == null) + { + processProducerObservers = findObservers(ProcessProducer.class); + } + if (processProducerObservers.isEmpty()) + { + return emptyList(); + } + } + // note: don't forget to update filterByExtensionEventType method too + } Type eventType = metadata.validatedType(); Collection<ObserverMethod<? super T>> observersMethods = filterByQualifiers( filterByType(event, eventType, isLifecycleEvent), metadata.getQualifiers()); @@ -197,8 +411,7 @@ public final class NotificationManager { observersMethods = filterByWithAnnotations(observersMethods, ((ProcessAnnotatedType) event).getAnnotatedType()); } - - if (!isLifecycleEvent && observersMethods.isEmpty()) + else if (!isLifecycleEvent && observersMethods.isEmpty()) { //this check for the TCK is only needed if no observer was found EventUtil.checkEventBindings(webBeansContext, metadata.getQualifiers()); @@ -367,7 +580,61 @@ public final class NotificationManager { Class<?> eventClass = ClassUtil.getClazz(eventType); Set<ObserverMethod<? super T>> matching = new HashSet<>(); - Set<Type> keySet = observers.keySet(); + final Map<Type, Set<ObserverMethod<?>>> sourceMap; + if (event instanceof ProcessAnnotatedType) // check resolveObservers + { + sourceMap = processAnnotatedTypeObservers; + } + else if (event instanceof ProcessSyntheticObserverMethod) + { + sourceMap = processSyntheticObserverMethodObservers; + } + else if (event instanceof ProcessObserverMethod) + { + sourceMap = processObserverMethodObservers; + } + else if (event instanceof ProcessProducerField) + { + sourceMap = processProducerFieldObservers; + } + else if (event instanceof ProcessProducerMethod) + { + sourceMap = processProducerMethodObservers; + } + else if (event instanceof ProcessSyntheticBean) + { + sourceMap = processSyntheticBeanObservers; + } + else if (event instanceof ProcessProducer) + { + sourceMap = processProducerObservers; + } + else if (event instanceof ProcessManagedBean) + { + sourceMap = processManagedBeanObservers; + } + else if (event instanceof ProcessBean) + { + sourceMap = processBeanObservers; + } + else if (event instanceof ProcessBeanAttributes) + { + sourceMap = processBeanAttributesObservers; + } + else if (event instanceof ProcessInjectionTarget) + { + sourceMap = processInjectionTargetObservers; + } + else if (event instanceof ProcessInjectionPoint) + { + sourceMap = processInjectionPointObservers; + } + else + { + sourceMap = observers; + } + + Set<Type> keySet = sourceMap.keySet(); for (Type type : keySet) { Class<?> beanClass; @@ -913,6 +1180,17 @@ public final class NotificationManager return webBeansContext.getWebBeansUtil().isContainerEventType(paramType); } + // for lifecycle parameterized events for now + private Map<Type, Set<ObserverMethod<?>>> findObservers(final Class<?> type) + { + return observers.entrySet().stream() + .filter(it -> { + final Class<?> keyType = ClassUtil.getClass(it.getKey()); + return type.isAssignableFrom(keyType); + }) + .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + // this behaves as a future aggregator, we don't strictly need to represent it but found it more expressive private static final class CDICompletionFuture<T> extends CompletableFuture<T> { diff --git a/webbeans-impl/src/test/java/org/apache/webbeans/test/portable/events/PortableEventTest.java b/webbeans-impl/src/test/java/org/apache/webbeans/test/portable/events/PortableEventTest.java index eef8b13..b2103d8 100644 --- a/webbeans-impl/src/test/java/org/apache/webbeans/test/portable/events/PortableEventTest.java +++ b/webbeans-impl/src/test/java/org/apache/webbeans/test/portable/events/PortableEventTest.java @@ -79,7 +79,6 @@ public class PortableEventTest extends AbstractUnitTest } @Test - public void testRawTypeExtension() { Collection<String> beanXmls = new ArrayList<String>();