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

jlmonteiro pushed a commit to branch owb_2.0.x
in repository https://gitbox.apache.org/repos/asf/openwebbeans.git

commit ab45ce695d8e87191132db0a6f2f4d60e5abb7e1
Author: Jean-Louis Monteiro <jlmonte...@tomitribe.com>
AuthorDate: Wed May 14 11:51:07 2025 +0200

    fix(#OWB-1450): Interceptor proxy memory leak. Add caching at an higher 
level
---
 .../TrackingAnnotatedTypeConfiguratorImpl.java     | 522 +++++++++++++++++++++
 .../container/InterceptionFactoryImpl.java         | 120 +++--
 .../webbeans/portable/AnnotatedTypeImpl.java       |   2 +-
 .../org/apache/webbeans/util/WebBeansUtil.java     |   8 +
 .../TrackingAnnotatedTypeConfiguratorImplTest.java | 251 ++++++++++
 .../InterceptorDecoratorProxyFactoryTest.java      |  74 ++-
 6 files changed, 944 insertions(+), 33 deletions(-)

diff --git 
a/webbeans-impl/src/main/java/org/apache/webbeans/configurator/TrackingAnnotatedTypeConfiguratorImpl.java
 
b/webbeans-impl/src/main/java/org/apache/webbeans/configurator/TrackingAnnotatedTypeConfiguratorImpl.java
new file mode 100644
index 000000000..4826c199d
--- /dev/null
+++ 
b/webbeans-impl/src/main/java/org/apache/webbeans/configurator/TrackingAnnotatedTypeConfiguratorImpl.java
@@ -0,0 +1,522 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.webbeans.configurator;
+
+import javax.enterprise.inject.spi.AnnotatedParameter;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.AnnotatedConstructor;
+import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import 
javax.enterprise.inject.spi.configurator.AnnotatedConstructorConfigurator;
+import javax.enterprise.inject.spi.configurator.AnnotatedFieldConfigurator;
+import javax.enterprise.inject.spi.configurator.AnnotatedMethodConfigurator;
+import javax.enterprise.inject.spi.configurator.AnnotatedParameterConfigurator;
+import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
+import org.apache.webbeans.portable.AnnotatedTypeImpl;
+
+import java.lang.annotation.Annotation;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class TrackingAnnotatedTypeConfiguratorImpl<T> implements 
AnnotatedTypeConfigurator<T>
+{
+
+    private final AnnotatedTypeConfiguratorImpl<T> delegate;
+    private final List<String> actions = new ArrayList<>();
+
+    public TrackingAnnotatedTypeConfiguratorImpl(final 
AnnotatedTypeConfiguratorImpl<T> delegate)
+    {
+        this.delegate = delegate;
+    }
+
+    public String getPassivationId()
+    {
+        return actions.stream().collect(Collectors.joining(">>"));
+    }
+
+    @Override
+    public final boolean equals(final Object o)
+    {
+        if (!(o instanceof TrackingAnnotatedTypeConfiguratorImpl))
+        {
+            return false;
+        }
+
+        final TrackingAnnotatedTypeConfiguratorImpl<?> that = 
(TrackingAnnotatedTypeConfiguratorImpl<?>) o;
+        return getPassivationId().equals(that.getPassivationId());
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return getPassivationId().hashCode();
+    }
+
+    @Override
+    public AnnotatedType<T> getAnnotated()
+    {
+        return delegate.getAnnotated();
+    }
+
+    @Override
+    public AnnotatedTypeConfigurator<T> add(Annotation annotation)
+    {
+        actions.add("+@" + annotation.annotationType());
+        delegate.add(annotation);
+        return this;
+    }
+
+    @Override
+    public AnnotatedTypeConfigurator<T> remove(Predicate<Annotation> predicate)
+    {
+        delegate.remove(a ->
+        {
+           if (predicate.test((Annotation) a))
+           {
+               actions.add("-@" + ((Annotation) a).annotationType());
+               return true;
+           }
+           return false;
+        });
+        return this;
+    }
+
+    @Override
+    public AnnotatedTypeConfigurator<T> removeAll()
+    {
+        actions.add("--");
+        delegate.removeAll();
+        return this;
+    }
+
+    @Override
+    public Set<AnnotatedMethodConfigurator<? super T>> methods()
+    {
+        return delegate.methods().stream()
+                       .map(m -> new 
TrackingAnnotatedMethodConfiguratorImpl<>(m, actions))
+                       .collect(Collectors.toSet());
+    }
+
+    @Override
+    public Stream<AnnotatedMethodConfigurator<? super T>> 
filterMethods(Predicate<AnnotatedMethod<? super T>> predicate)
+    {
+        return delegate.filterMethods(a ->
+        {
+            if (predicate.test(a))
+            {
+                actions.add("-m@" + a);
+                return true;
+            }
+            return false;
+        });
+    }
+
+    @Override
+    public Set<AnnotatedFieldConfigurator<? super T>> fields()
+    {
+        return delegate.fields().stream()
+                       .map(f -> new 
TrackingAnnotatedFieldConfiguratorImpl<>(f, actions))
+                       .collect(Collectors.toSet());
+    }
+
+    @Override
+    public Stream<AnnotatedFieldConfigurator<? super T>> 
filterFields(Predicate<AnnotatedField<? super T>> predicate)
+    {
+        return delegate.filterFields(a ->
+        {
+            if (predicate.test(a))
+            {
+                actions.add("-f@" + a);
+                return true;
+            }
+            return false;
+        });
+    }
+
+    @Override
+    public Set<AnnotatedConstructorConfigurator<T>> constructors()
+    {
+        return delegate.constructors().stream()
+                       .map(c -> new 
TrackingAnnotatedConstructorConfiguratorImpl<>(c, actions))
+                       .collect(Collectors.toSet());
+    }
+
+    @Override
+    public Stream<AnnotatedConstructorConfigurator<T>> 
filterConstructors(Predicate<AnnotatedConstructor<T>> predicate)
+    {
+        return delegate.filterConstructors(a ->
+        {
+            if (predicate.test(a))
+            {
+                actions.add("-c@" + a);
+                return true;
+            }
+            return false;
+        });
+    }
+
+
+    public AnnotatedTypeImpl<T> getNewAnnotatedType()
+    {
+        return delegate.getNewAnnotatedType();
+    }
+
+    public static class TrackingAnnotatedFieldConfiguratorImpl<T> implements 
AnnotatedFieldConfigurator<T>
+    {
+        private final AnnotatedFieldConfigurator<T> delegate;
+        private final List<String> actions;
+
+        public TrackingAnnotatedFieldConfiguratorImpl(final 
AnnotatedFieldConfigurator<T> delegate,
+                                                      final List<String> 
actions)
+        {
+            this.delegate = delegate;
+            this.actions = actions;
+            this.actions.add("\n\t");
+        }
+
+        @Override
+        public final boolean equals(final Object o)
+        {
+            if (!(o instanceof TrackingAnnotatedFieldConfiguratorImpl))
+            {
+                return false;
+            }
+
+            final TrackingAnnotatedFieldConfiguratorImpl<?> that = 
(TrackingAnnotatedFieldConfiguratorImpl<?>) o;
+            return delegate.equals(that.delegate);
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return delegate.hashCode();
+        }
+
+        @Override
+        public String toString()
+        {
+            return "TrackingField(" + delegate + ")";
+        }
+
+        @Override
+        public AnnotatedField<T> getAnnotated()
+        {
+            return delegate.getAnnotated();
+        }
+
+        @Override
+        public AnnotatedFieldConfigurator<T> removeAll()
+        {
+            actions.add("--");
+            delegate.removeAll();
+            return this;
+        }
+
+        @Override
+        public AnnotatedFieldConfigurator<T> remove(final 
Predicate<Annotation> predicate)
+        {
+            delegate.remove(a ->
+            {
+                if (predicate.test(a))
+                {
+                    actions.add("-@" + a);
+                    return true;
+                }
+                return false;
+            });
+            return this;
+        }
+
+        @Override
+        public AnnotatedFieldConfigurator<T> add(final Annotation annotation)
+        {
+            actions.add("+@" + annotation.annotationType());
+            delegate.add(annotation);
+            return this;
+        }
+    }
+
+    public static class TrackingAnnotatedMethodConfiguratorImpl<T> implements 
AnnotatedMethodConfigurator<T>
+    {
+        private final AnnotatedMethodConfigurator<T> delegate;
+        private final List<String> actions;
+
+        public TrackingAnnotatedMethodConfiguratorImpl(final 
AnnotatedMethodConfigurator<T> delegate,
+                                                       final List<String> 
actions)
+        {
+            this.delegate = delegate;
+            this.actions = actions;
+            this.actions.add("\n\t");
+        }
+
+        @Override
+        public final boolean equals(final Object o)
+        {
+            if (!(o instanceof TrackingAnnotatedMethodConfiguratorImpl))
+            {
+                return false;
+            }
+
+            final TrackingAnnotatedMethodConfiguratorImpl<?> that = 
(TrackingAnnotatedMethodConfiguratorImpl<?>) o;
+            return delegate.equals(that.delegate);
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return delegate.hashCode();
+        }
+
+        @Override
+        public String toString()
+        {
+            return "TrackingMethod(" + delegate + ")";
+        }
+
+        @Override
+        public AnnotatedMethodConfigurator<T> add(final Annotation annotation)
+        {
+            actions.add("+@" + annotation.annotationType());
+            delegate.add(annotation);
+            return this;
+        }
+
+        @Override
+        public Stream<AnnotatedParameterConfigurator<T>> filterParams(final 
Predicate<AnnotatedParameter<T>> predicate)
+        {
+            return delegate.filterParams(a ->
+            {
+                if (predicate.test(a))
+                {
+                    actions.add("-p@" + a);
+                    return true;
+                }
+                return false;
+            });
+        }
+
+        @Override
+        public AnnotatedMethod<T> getAnnotated()
+        {
+            return delegate.getAnnotated();
+        }
+
+        @Override
+        public List<AnnotatedParameterConfigurator<T>> params()
+        {
+            return delegate.params().stream()
+                           .map(p -> new 
TrackingAnnotatedParameterConfiguratorImpl(p, actions))
+                           .map(p -> (AnnotatedParameterConfigurator<T>) p)
+                           .collect(Collectors.toList());
+        }
+
+        @Override
+        public AnnotatedMethodConfigurator<T> remove(final 
Predicate<Annotation> predicate)
+        {
+            delegate.remove(a ->
+            {
+                if (predicate.test(a))
+                {
+                    actions.add("-@" + a.toString());
+                    return true;
+                }
+                return false;
+            });
+            return this;
+        }
+
+        @Override
+        public AnnotatedMethodConfigurator<T> removeAll()
+        {
+            actions.add("--");
+            delegate.removeAll();
+            return this;
+        }
+    }
+
+    public static class TrackingAnnotatedConstructorConfiguratorImpl<T> 
implements AnnotatedConstructorConfigurator<T>
+    {
+        private final AnnotatedConstructorConfigurator<T> delegate;
+        private final List<String> actions;
+
+        public TrackingAnnotatedConstructorConfiguratorImpl(final 
AnnotatedConstructorConfigurator<T> delegate,
+                                                            final List<String> 
actions)
+        {
+            this.delegate = delegate;
+            this.actions = actions;
+            this.actions.add("\n\t");
+        }
+
+        @Override
+        public final boolean equals(final Object o)
+        {
+            if (!(o instanceof TrackingAnnotatedConstructorConfiguratorImpl))
+            {
+                return false;
+            }
+
+            final TrackingAnnotatedConstructorConfiguratorImpl<?> that =
+                (TrackingAnnotatedConstructorConfiguratorImpl<?>) o;
+            return delegate.equals(that.delegate);
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return delegate.hashCode();
+        }
+
+        @Override
+        public String toString()
+        {
+            return "TrackingConstructor(" + delegate + ")";
+        }
+
+        @Override
+        public AnnotatedConstructor<T> getAnnotated()
+        {
+            return delegate.getAnnotated();
+        }
+
+        @Override
+        public AnnotatedConstructorConfigurator<T> add(final Annotation 
annotation)
+        {
+            actions.add("+@" + annotation.annotationType());
+            delegate.add(annotation);
+            return this;
+        }
+
+        @Override
+        public AnnotatedConstructorConfigurator<T> remove(final 
Predicate<Annotation> predicate)
+        {
+            actions.add("-@" + predicate.toString());
+            delegate.remove(predicate);
+            return this;
+        }
+
+        @Override
+        public AnnotatedConstructorConfigurator<T> removeAll()
+        {
+            actions.add("--");
+            delegate.removeAll();
+            return this;
+        }
+
+        @Override
+        public List<AnnotatedParameterConfigurator<T>> params()
+        {
+            return delegate.params().stream()
+                           .map(p -> new 
TrackingAnnotatedParameterConfiguratorImpl(p, actions))
+                            .map(p -> (AnnotatedParameterConfigurator<T>) p)
+                           .collect(Collectors.toList());
+        }
+
+        @Override
+        public Stream<AnnotatedParameterConfigurator<T>> filterParams(final 
Predicate<AnnotatedParameter<T>> predicate)
+        {
+            return delegate.filterParams(a ->
+             {
+                if (predicate.test(a))
+                {
+                    actions.add("-p@" + a.toString());
+                    return true;
+                }
+                return false;
+            });
+        }
+    }
+
+    public static class TrackingAnnotatedParameterConfiguratorImpl<T> 
implements AnnotatedParameterConfigurator<T>
+    {
+        private final AnnotatedParameterConfigurator<T> delegate;
+        private final List<String> actions;
+
+        public TrackingAnnotatedParameterConfiguratorImpl(final 
AnnotatedParameterConfigurator<T> delegate,
+                                                          final List<String> 
actions)
+        {
+            this.delegate = delegate;
+            this.actions = actions;
+            this.actions.add("\n\t");
+        }
+
+        @Override
+        public final boolean equals(final Object o)
+        {
+            if (!(o instanceof TrackingAnnotatedParameterConfiguratorImpl))
+            {
+                return false;
+            }
+
+            final TrackingAnnotatedParameterConfiguratorImpl<?> that =
+                (TrackingAnnotatedParameterConfiguratorImpl<?>) o;
+            return delegate.equals(that.delegate);
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return delegate.hashCode();
+        }
+
+        @Override
+        public String toString()
+        {
+            return "TrackingParameter(" + delegate + ")";
+        }
+
+        @Override
+        public AnnotatedParameterConfigurator<T> add(final Annotation 
annotation)
+        {
+            actions.add("+@" + annotation.annotationType());
+            delegate.add(annotation);
+            return this;
+        }
+
+        @Override
+        public AnnotatedParameter<T> getAnnotated()
+        {
+            return delegate.getAnnotated();
+        }
+
+        @Override
+        public AnnotatedParameterConfigurator<T> remove(final 
Predicate<Annotation> predicate)
+        {
+            delegate.remove(a ->
+            {
+                if (predicate.test(a))
+                {
+                    actions.add("-p@" + a.toString());
+                    return true;
+                }
+                return false;
+            });
+            return this;
+        }
+
+        @Override
+        public AnnotatedParameterConfigurator<T> removeAll()
+        {
+            actions.add("--");
+            delegate.removeAll();
+            return this;
+        }
+    }
+}
\ No newline at end of file
diff --git 
a/webbeans-impl/src/main/java/org/apache/webbeans/container/InterceptionFactoryImpl.java
 
b/webbeans-impl/src/main/java/org/apache/webbeans/container/InterceptionFactoryImpl.java
index 3064902d3..0262f925f 100644
--- 
a/webbeans-impl/src/main/java/org/apache/webbeans/container/InterceptionFactoryImpl.java
+++ 
b/webbeans-impl/src/main/java/org/apache/webbeans/container/InterceptionFactoryImpl.java
@@ -20,28 +20,30 @@ package org.apache.webbeans.container;
 
 import org.apache.webbeans.config.WebBeansContext;
 import org.apache.webbeans.configurator.AnnotatedTypeConfiguratorImpl;
+import org.apache.webbeans.configurator.TrackingAnnotatedTypeConfiguratorImpl;
 import org.apache.webbeans.context.creational.CreationalContextImpl;
 import org.apache.webbeans.intercept.InterceptorResolutionService;
-import org.apache.webbeans.portable.AnnotatedTypeImpl;
 import org.apache.webbeans.proxy.InterceptorDecoratorProxyFactory;
 import org.apache.webbeans.util.WebBeansUtil;
 
 import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.InterceptionFactory;
-import javax.enterprise.inject.spi.Interceptor;
 import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+import static java.util.Optional.ofNullable;
 
 public class InterceptionFactoryImpl<T> implements InterceptionFactory<T> 
/*todo: make it serializable*/
 {
     private final CreationalContextImpl<T> creationalContext;
-    private final AnnotatedTypeConfiguratorImpl<T> configurator;
     private final Set<Annotation> qualifiers;
     private final WebBeansContext context;
+    private final AnnotatedType<T> at;
+    private TrackingAnnotatedTypeConfiguratorImpl<T> configurator;
     private boolean ignoreFinals;
     private volatile boolean called;
 
@@ -49,9 +51,10 @@ public class InterceptionFactoryImpl<T> implements 
InterceptionFactory<T> /*todo
                                    Set<Annotation> qualifiers, 
CreationalContextImpl<T> cc)
     {
         this.context = context;
-        this.configurator = new AnnotatedTypeConfiguratorImpl<>(context, at);
+        this.configurator = null; // computed later
         this.qualifiers = qualifiers;
         this.creationalContext = cc;
+        this.at = at;
     }
 
     @Override
@@ -64,6 +67,12 @@ public class InterceptionFactoryImpl<T> implements 
InterceptionFactory<T> /*todo
     @Override
     public AnnotatedTypeConfigurator<T> configure()
     {
+        if (configurator == null)
+        {
+            // configurator = new AnnotatedTypeConfiguratorImpl<>(context, at);
+            AnnotatedTypeConfiguratorImpl<T> realConfig = new 
AnnotatedTypeConfiguratorImpl<>(context, at);
+            configurator = new 
TrackingAnnotatedTypeConfiguratorImpl<>(realConfig);
+        }
         return configurator;
     }
 
@@ -72,31 +81,42 @@ public class InterceptionFactoryImpl<T> implements 
InterceptionFactory<T> /*todo
     {
         check();
 
-        ClassLoader classLoader = originalInstance.getClass().getClassLoader();
-        if (classLoader == null)
-        {
-            classLoader = WebBeansUtil.getCurrentClassLoader();
-        }
-
-        InterceptorDecoratorProxyFactory factory = 
context.getInterceptorDecoratorProxyFactory();
-        AnnotatedTypeImpl<T> newAnnotatedType = 
configurator.getNewAnnotatedType();
-        InterceptorResolutionService.BeanInterceptorInfo interceptorInfo =
-                context.getInterceptorResolutionService()
-                    
.calculateInterceptorInfo(newAnnotatedType.getTypeClosure(), qualifiers, 
newAnnotatedType, !ignoreFinals);
-        Class<T> subClass = factory.createProxyClass(interceptorInfo, 
newAnnotatedType, classLoader);
+        final ClassLoader classLoader = 
ofNullable(originalInstance.getClass().getClassLoader())
+                .orElseGet(WebBeansUtil::getCurrentClassLoader);
 
-        Map<Interceptor<?>,Object> interceptorInstances  = 
context.getInterceptorResolutionService()
-                .createInterceptorInstances(interceptorInfo, 
creationalContext);
+        AnnotatedType<T> newAnnotatedType = configurator == null ? at : 
configurator.getNewAnnotatedType();
+        newAnnotatedType.getTypeClosure(); // make sure the toString bellow is 
accurate
+        String passivationId = InterceptionFactory.class.getName() + ">>" + 
newAnnotatedType + "<<" + ignoreFinals;
 
-        Map<Method, List<Interceptor<?>>> methodInterceptors =
-                
context.getInterceptorResolutionService().createMethodInterceptors(interceptorInfo);
+        // if configure() has not been called, we need to create a new 
configurator with the muted annotated type
+        if (configurator != null) // meaning app changed dynamically the 
annotated type configuration
+        {
+            passivationId = passivationId + ">>" + 
configurator.getPassivationId();
+        }
 
-        // this is a good question actually, should we even support it?
-        String passivationId = InterceptionFactory.class.getName() + ">>" + 
newAnnotatedType.toString();
+        InterceptorResolutionService interceptorResolutionService = 
context.getInterceptorResolutionService();
+        InterceptionFactoryCacheEntry cache = context
+                .getWebBeansUtil()
+                .getInterceptionFactoryCache()
+                .computeIfAbsent(passivationId, () -> {
+                    InterceptorResolutionService.BeanInterceptorInfo 
interceptorInfo =
+                            interceptorResolutionService
+                                    
.calculateInterceptorInfo(newAnnotatedType.getTypeClosure(), qualifiers, 
newAnnotatedType, !ignoreFinals);
+                    InterceptorDecoratorProxyFactory factory = 
context.getInterceptorDecoratorProxyFactory();
+                    return new InterceptionFactoryCacheEntry(
+                            factory.createProxyClass(interceptorInfo, 
newAnnotatedType, classLoader),
+                            interceptorInfo);
+                });
 
-        return context.getInterceptorResolutionService().createProxiedInstance(
-                originalInstance, creationalContext, creationalContext, 
interceptorInfo, subClass,
-                methodInterceptors, passivationId, interceptorInstances, c -> 
false, (a, d) -> d);
+        Map<javax.enterprise.inject.spi.Interceptor<?>, Object> 
interceptorInstances  = interceptorResolutionService
+            .createInterceptorInstances(cache.interceptorInfo, 
creationalContext);
+        Map<java.lang.reflect.Method, 
java.util.List<javax.enterprise.inject.spi.Interceptor<?>>> methodInterceptors =
+            
interceptorResolutionService.createMethodInterceptors(cache.interceptorInfo);
+        return interceptorResolutionService.createProxiedInstance(
+                originalInstance, creationalContext, creationalContext, 
cache.interceptorInfo,
+                (Class<? extends T>) cache.proxyClass,
+                methodInterceptors, passivationId, interceptorInstances,
+                c -> false, (a, d) -> d);
     }
 
     private void check()
@@ -118,4 +138,50 @@ public class InterceptionFactoryImpl<T> implements 
InterceptionFactory<T> /*todo
             throw new IllegalStateException("createInterceptedInstance() can 
be called only once");
         }
     }
+
+    public static class InterceptionFactoryCache
+    {
+        private final Map<String, InterceptionFactoryCacheEntry> cache = new 
ConcurrentHashMap<>();
+
+        private InterceptionFactoryCacheEntry computeIfAbsent(
+                final String interceptionFactoryCacheKey, final 
Supplier<InterceptionFactoryCacheEntry> compute)
+        {
+            InterceptionFactoryCacheEntry entry = 
cache.get(interceptionFactoryCacheKey);
+            if (entry == null)
+            {
+                // we do not want to create twice a proxy class,
+                // "bottleneck" but quickly cached
+                // so "ok"ish
+                synchronized (this)
+                {
+                    entry = cache.get(interceptionFactoryCacheKey);
+                    if (entry == null)
+                    {
+                        entry = compute.get();
+                        cache.putIfAbsent(interceptionFactoryCacheKey, entry);
+                    }
+                }
+            }
+            return entry;
+        }
+
+        public int size()
+        {
+            return cache.size();
+        }
+    }
+
+    private static class InterceptionFactoryCacheEntry
+    {
+        private final Class<?> proxyClass;
+        private final InterceptorResolutionService.BeanInterceptorInfo 
interceptorInfo;
+
+        private InterceptionFactoryCacheEntry(
+                final Class<?> proxyClass,
+                final InterceptorResolutionService.BeanInterceptorInfo 
interceptorInfo)
+        {
+            this.proxyClass = proxyClass;
+            this.interceptorInfo = interceptorInfo;
+        }
+    }
 }
diff --git 
a/webbeans-impl/src/main/java/org/apache/webbeans/portable/AnnotatedTypeImpl.java
 
b/webbeans-impl/src/main/java/org/apache/webbeans/portable/AnnotatedTypeImpl.java
index 1610b745b..e26ec7fec 100644
--- 
a/webbeans-impl/src/main/java/org/apache/webbeans/portable/AnnotatedTypeImpl.java
+++ 
b/webbeans-impl/src/main/java/org/apache/webbeans/portable/AnnotatedTypeImpl.java
@@ -209,7 +209,7 @@ public class AnnotatedTypeImpl<X>
     @Override
     public int hashCode() // enough, no need to create a hashcode from 
attributes
     {
-        return super.hashCode();
+        return getJavaClass().hashCode();
     }
 
     private State getState()
diff --git 
a/webbeans-impl/src/main/java/org/apache/webbeans/util/WebBeansUtil.java 
b/webbeans-impl/src/main/java/org/apache/webbeans/util/WebBeansUtil.java
index cbd36388a..b30ed3ed5 100644
--- a/webbeans-impl/src/main/java/org/apache/webbeans/util/WebBeansUtil.java
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/util/WebBeansUtil.java
@@ -55,6 +55,7 @@ import org.apache.webbeans.config.OwbWildcardTypeImpl;
 import org.apache.webbeans.config.WebBeansContext;
 import org.apache.webbeans.container.AnnotatedTypeWrapper;
 import org.apache.webbeans.container.InjectionResolver;
+import org.apache.webbeans.container.InterceptionFactoryImpl;
 import 
org.apache.webbeans.context.control.ActivateRequestContextInterceptorBean;
 import org.apache.webbeans.context.control.RequestContextControllerBean;
 import org.apache.webbeans.exception.WebBeansConfigurationException;
@@ -173,6 +174,8 @@ public final class WebBeansUtil
     private final ConcurrentMap<EventCacheKey, Boolean> validEventType = new 
ConcurrentHashMap<>();
     private final ConcurrentMap<Type, Boolean> notContainerEvents = new 
ConcurrentHashMap<>();
 
+    private final InterceptionFactoryImpl.InterceptionFactoryCache 
interceptionFactoryCache = new 
InterceptionFactoryImpl.InterceptionFactoryCache();
+
     private InstanceBean instanceBean;
     private EventBean eventBean;
 
@@ -1762,6 +1765,11 @@ public final class WebBeansUtil
         }
     }
 
+    public InterceptionFactoryImpl.InterceptionFactoryCache 
getInterceptionFactoryCache()
+    {
+        return interceptionFactoryCache;
+    }
+
     public InterceptionFactoryBean getInterceptionFactoryBean()
     {
         return new InterceptionFactoryBean(webBeansContext);
diff --git 
a/webbeans-impl/src/test/java/org/apache/webbeans/configurator/TrackingAnnotatedTypeConfiguratorImplTest.java
 
b/webbeans-impl/src/test/java/org/apache/webbeans/configurator/TrackingAnnotatedTypeConfiguratorImplTest.java
new file mode 100644
index 000000000..23281de3f
--- /dev/null
+++ 
b/webbeans-impl/src/test/java/org/apache/webbeans/configurator/TrackingAnnotatedTypeConfiguratorImplTest.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.webbeans.configurator;
+
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.test.AbstractUnitTest;
+import org.junit.Test;
+
+import javax.enterprise.inject.spi.AnnotatedConstructor;
+import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedParameter;
+import javax.enterprise.inject.spi.AnnotatedType;
+import 
javax.enterprise.inject.spi.configurator.AnnotatedConstructorConfigurator;
+import javax.enterprise.inject.spi.configurator.AnnotatedFieldConfigurator;
+import javax.enterprise.inject.spi.configurator.AnnotatedMethodConfigurator;
+import javax.enterprise.inject.spi.configurator.AnnotatedParameterConfigurator;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.Assert.assertTrue;
+
+
+public class TrackingAnnotatedTypeConfiguratorImplTest extends 
AbstractUnitTest {
+
+    @Test
+    public void shouldTrackAllConfiguratorActions() {
+        AnnotatedTypeConfiguratorImpl<MyBean> base = new 
FullStubTypeConfigurator<>();
+
+        TrackingAnnotatedTypeConfiguratorImpl<MyBean> tracking = new 
TrackingAnnotatedTypeConfiguratorImpl<>(base);
+
+        tracking.add(getDummyAnn());
+        tracking.remove(a -> a.annotationType().equals(DummyAnn.class));
+        tracking.removeAll();
+
+        tracking.fields().forEach(f -> {
+            f.add(getDummyAnn());
+            f.remove(a -> a.annotationType().equals(DummyAnn.class));
+            f.removeAll();
+        });
+
+        tracking.methods().forEach(m -> {
+            m.add(getDummyAnn());
+            m.remove(a -> a.annotationType().equals(DummyAnn.class));
+            m.removeAll();
+
+            m.params().forEach(p -> {
+                p.add(getDummyAnn());
+                p.remove(a -> a.annotationType().equals(DummyAnn.class));
+                p.removeAll();
+            });
+        });
+
+        tracking.constructors().forEach(c -> {
+            c.add(getDummyAnn());
+            c.remove(a -> a.annotationType().equals(DummyAnn.class));
+            c.removeAll();
+
+            c.params().forEach(p -> {
+                p.add(getDummyAnn());
+                p.remove(a -> a.annotationType().equals(DummyAnn.class));
+                p.removeAll();
+            });
+        });
+
+        tracking.filterFields(f -> true).collect(Collectors.toList());
+        tracking.filterMethods(m -> true).collect(Collectors.toList());
+        tracking.filterConstructors(c -> true).collect(Collectors.toList());
+
+        final String passivationId = tracking.getPassivationId();
+
+        assertTrue(passivationId.contains("+@interface " + 
DummyAnn.class.getName()));
+        assertTrue(passivationId.contains("--")); // at least one removeAll()
+        assertTrue(passivationId.contains("-@")); // from any predicate 
matched removal
+        assertTrue(passivationId.contains("-f@")); // filterFields
+        assertTrue(passivationId.contains("-m@")); // filterMethods
+        assertTrue(passivationId.contains("-c@")); // filterConstructors
+        assertTrue(passivationId.contains("-p@")); // param filter
+    }
+
+    public static Annotation getDummyAnn() {
+        return WithDummyAnn.class.getAnnotation(DummyAnn.class);
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface DummyAnn {}
+
+    @DummyAnn
+    static class WithDummyAnn {}
+
+    public static class MyBean {
+        @DummyAnn
+        public String field;
+
+        @DummyAnn
+        public void method(@DummyAnn String param) {}
+
+        @DummyAnn
+        public MyBean() {}
+    }
+
+    public static class FullStubTypeConfigurator<T> extends 
AnnotatedTypeConfiguratorImpl<MyBean> {
+        public FullStubTypeConfigurator() {
+            super(WebBeansContext.currentInstance(),
+                  WebBeansContext.currentInstance()
+                                 .getBeanManagerImpl()
+                                 .createAnnotatedType(MyBean.class));
+        }
+
+        @Override
+        public Set<AnnotatedFieldConfigurator<? super MyBean>> fields() {
+            Set<AnnotatedFieldConfigurator<? super MyBean>> 
annotatedFieldConfigurators = new HashSet<>();
+            annotatedFieldConfigurators.add(new FullStubFieldConfigurator<>());
+            return annotatedFieldConfigurators;
+        }
+
+        @Override
+        public Set<AnnotatedMethodConfigurator<? super MyBean>> methods() {
+            Set<AnnotatedMethodConfigurator<? super MyBean>> 
annotatedMethodConfigurators = new HashSet<>();
+            annotatedMethodConfigurators.add(new 
FullStubMethodConfigurator<>());
+            return annotatedMethodConfigurators;
+        }
+
+        @Override
+        public Set<AnnotatedConstructorConfigurator<MyBean>> constructors() {
+            Set<AnnotatedConstructorConfigurator<MyBean>> 
annotatedConstructorConfigurators = new HashSet<>();
+            annotatedConstructorConfigurators.add(new 
FullStubConstructorConfigurator<>());
+            return annotatedConstructorConfigurators;
+        }
+
+        @Override
+        public Stream<AnnotatedFieldConfigurator<? super MyBean>> 
filterFields(Predicate<AnnotatedField<?
+            super MyBean>> p) {
+            p.test(null);
+            return fields().stream();
+        }
+
+        @Override
+        public Stream<AnnotatedMethodConfigurator<? super MyBean>> 
filterMethods(Predicate<AnnotatedMethod<?
+            super MyBean>> p) {
+            p.test(null);
+            return methods().stream();
+        }
+
+        @Override
+        public Stream<AnnotatedConstructorConfigurator<MyBean>> 
filterConstructors(Predicate<AnnotatedConstructor<MyBean>> p) {
+            p.test(null);
+            return constructors().stream();
+        }
+
+        @Override
+        public AnnotatedType<MyBean> getAnnotated() {
+            return super.getAnnotated();
+        }
+    }
+
+    public static class FullStubFieldConfigurator<T> implements 
AnnotatedFieldConfigurator<MyBean> {
+        @Override public AnnotatedField<MyBean> getAnnotated() {return null;}
+
+        @Override public AnnotatedFieldConfigurator<MyBean> removeAll() 
{return this;}
+
+        @Override public AnnotatedFieldConfigurator<MyBean> 
remove(Predicate<Annotation> predicate) {
+            predicate.test(getDummyAnn()); return this;
+        }
+
+        @Override public AnnotatedFieldConfigurator<MyBean> add(Annotation 
annotation) {return this;}
+    }
+
+    public static class FullStubMethodConfigurator<T> implements 
AnnotatedMethodConfigurator<MyBean> {
+        @Override public AnnotatedMethodConfigurator<MyBean> add(Annotation 
annotation) {return this;}
+
+        @Override
+        public Stream<AnnotatedParameterConfigurator<MyBean>> 
filterParams(Predicate<AnnotatedParameter<MyBean>> p) {
+            p.test(null); return params().stream();
+        }
+
+        @Override public AnnotatedMethod<MyBean> getAnnotated() {return null;}
+
+        @Override public List<AnnotatedParameterConfigurator<MyBean>> params() 
{
+            List<AnnotatedParameterConfigurator<MyBean>> 
annotatedParameterConfigurators = new ArrayList<>();
+            annotatedParameterConfigurators.add(new 
FullStubParamConfigurator<>());
+            return annotatedParameterConfigurators;
+        }
+
+        @Override public AnnotatedMethodConfigurator<MyBean> 
remove(Predicate<Annotation> predicate) {
+            predicate.test(getDummyAnn()); return this;
+        }
+
+        @Override public AnnotatedMethodConfigurator<MyBean> removeAll() 
{return this;}
+    }
+
+    public static class FullStubConstructorConfigurator<T> implements 
AnnotatedConstructorConfigurator<MyBean> {
+        @Override public AnnotatedConstructor<MyBean> getAnnotated() {return 
null;}
+
+        @Override public AnnotatedConstructorConfigurator<MyBean> 
add(Annotation annotation) {return this;}
+
+        @Override
+        public AnnotatedConstructorConfigurator<MyBean> 
remove(Predicate<Annotation> predicate) {
+            predicate.test(getDummyAnn()); return this;
+        }
+
+        @Override public AnnotatedConstructorConfigurator<MyBean> removeAll() 
{return this;}
+
+        @Override public List<AnnotatedParameterConfigurator<MyBean>> params() 
{
+            List<AnnotatedParameterConfigurator<MyBean>> 
annotatedParameterConfigurators = new ArrayList<>();
+            annotatedParameterConfigurators.add(new 
FullStubParamConfigurator<>());
+            return annotatedParameterConfigurators;
+        }
+
+        @Override
+        public Stream<AnnotatedParameterConfigurator<MyBean>> 
filterParams(Predicate<AnnotatedParameter<MyBean>> p) {
+            p.test(null); return params().stream();
+        }
+    }
+
+    public static class FullStubParamConfigurator<T> implements 
AnnotatedParameterConfigurator<MyBean> {
+        @Override public AnnotatedParameterConfigurator<MyBean> add(Annotation 
annotation) {return this;}
+
+        @Override public AnnotatedParameter<MyBean> getAnnotated() {return 
null;}
+
+        @Override public AnnotatedParameterConfigurator<MyBean> 
remove(Predicate<Annotation> predicate) {
+            predicate.test(getDummyAnn()); return this;
+        }
+
+        @Override public AnnotatedParameterConfigurator<MyBean> removeAll() 
{return this;}
+    }
+}
\ No newline at end of file
diff --git 
a/webbeans-impl/src/test/java/org/apache/webbeans/test/interceptors/factory/InterceptorDecoratorProxyFactoryTest.java
 
b/webbeans-impl/src/test/java/org/apache/webbeans/test/interceptors/factory/InterceptorDecoratorProxyFactoryTest.java
index 887342b62..b5737e58c 100644
--- 
a/webbeans-impl/src/test/java/org/apache/webbeans/test/interceptors/factory/InterceptorDecoratorProxyFactoryTest.java
+++ 
b/webbeans-impl/src/test/java/org/apache/webbeans/test/interceptors/factory/InterceptorDecoratorProxyFactoryTest.java
@@ -32,25 +32,52 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.InjectionPoint;
+import javax.enterprise.inject.spi.InterceptionFactory;
+import javax.enterprise.util.AnnotationLiteral;
+import javax.inject.Qualifier;
+import org.apache.webbeans.annotation.EmptyAnnotationLiteral;
 import org.apache.webbeans.config.WebBeansContext;
 import org.apache.webbeans.exception.WebBeansException;
-import org.apache.webbeans.test.AbstractUnitTest;
-import 
org.apache.webbeans.test.component.intercept.webbeans.TransactionalInterceptor;
-import 
org.apache.webbeans.test.interceptors.factory.beans.ClassInterceptedClass;
 import org.apache.webbeans.proxy.InterceptorDecoratorProxyFactory;
-
 import org.apache.webbeans.proxy.InterceptorHandler;
 import org.apache.webbeans.proxy.OwbInterceptorProxy;
+import org.apache.webbeans.test.AbstractUnitTest;
+import 
org.apache.webbeans.test.component.intercept.webbeans.TransactionalInterceptor;
+import 
org.apache.webbeans.test.interceptors.factory.beans.ClassInterceptedClass;
 import 
org.apache.webbeans.test.interceptors.factory.beans.TonsOfMethodsInterceptedClass;
-import org.apache.webbeans.util.ClassUtil;
 import org.apache.webbeans.test.util.CustomBaseType;
 import org.apache.webbeans.test.util.CustomType;
 import org.apache.webbeans.test.util.ExtendedSpecificClass;
 import org.apache.webbeans.test.util.GenericInterface;
 import org.apache.webbeans.test.util.SpecificClass;
+import org.apache.webbeans.util.ClassUtil;
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+
 
 /**
  * Test the {@link org.apache.webbeans.proxy.InterceptorDecoratorProxyFactory}
@@ -58,6 +85,35 @@ import org.junit.Test;
 public class InterceptorDecoratorProxyFactoryTest extends AbstractUnitTest
 {
 
+    @ApplicationScoped
+    public static class IFProducer {
+        @Produces
+        public Runnable wrap1(final InterceptionFactory<Runnable> 
interceptionFactory) {
+            return interceptionFactory.createInterceptedInstance(() -> {});
+        }
+
+        @Produces
+        @SimpleQualifier
+        public Runnable wrap2(final InterceptionFactory<Runnable> 
interceptionFactory) {
+            interceptionFactory.configure().add(new 
EmptyAnnotationLiteral<SimpleQualifier>(){});
+            return interceptionFactory.createInterceptedInstance(() -> {});
+        }
+    }
+
+    @Test
+    public void testEnsureOneProxyPerAT() {
+        startContainer(IFProducer.class);
+        final AnnotationLiteral<SimpleQualifier> simpleQualifier = new 
EmptyAnnotationLiteral<SimpleQualifier>(){};
+        final Runnable r11 = getInstance(Runnable.class);
+        final Runnable r12 = getInstance(Runnable.class);
+        assertEquals(1, 
getWebBeansContext().getWebBeansUtil().getInterceptionFactoryCache().size());
+        final Runnable r2 = getInstance(Runnable.class, simpleQualifier);
+        assertEquals(2, 
getWebBeansContext().getWebBeansUtil().getInterceptionFactoryCache().size());
+        assertSame(r11.getClass(), r12.getClass());
+        assertNotSame(r2.getClass(), r11.getClass());
+        assertSame(getInstance(Runnable.class, simpleQualifier).getClass(), 
r2.getClass());
+    }
+
     @Test
     public void testSimpleProxyCreation() throws Exception
     {
@@ -252,4 +308,12 @@ public class InterceptorDecoratorProxyFactoryTest extends 
AbstractUnitTest
             }
         }
     }
+
+    @Target({ METHOD })
+    @Retention(RUNTIME)
+    @Documented
+    @Qualifier
+    public @interface SimpleQualifier
+    {
+    }
 }


Reply via email to