You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwebbeans.apache.org by rm...@apache.org on 2020/12/16 13:33:21 UTC

[openwebbeans] branch master updated: [OWB-1357] skip extension events resolution when there is no observer for them

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 <rm...@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>();