This is an automated email from the ASF dual-hosted git repository.
jlmonteiro pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openwebbeans.git
The following commit(s) were added to refs/heads/main by this push:
new 18f37f888 fix(#OWB-1450): Interceptor proxy memory leak. Add caching
at an higher level
18f37f888 is described below
commit 18f37f8883d079d007d23dee429faf0cd79a201f
Author: Jean-Louis Monteiro <[email protected]>
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 | 119 +++--
.../webbeans/portable/AnnotatedTypeImpl.java | 2 +-
.../proxy/InterceptorDecoratorProxyFactory.java | 13 -
.../org/apache/webbeans/util/WebBeansUtil.java | 8 +
.../TrackingAnnotatedTypeConfiguratorImplTest.java | 200 ++++++++
.../InterceptorDecoratorProxyFactoryTest.java | 80 +++-
7 files changed, 888 insertions(+), 56 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..73c61313e
--- /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 jakarta.enterprise.inject.spi.AnnotatedParameter;
+import jakarta.enterprise.inject.spi.AnnotatedType;
+import jakarta.enterprise.inject.spi.AnnotatedConstructor;
+import jakarta.enterprise.inject.spi.AnnotatedField;
+import jakarta.enterprise.inject.spi.AnnotatedMethod;
+import
jakarta.enterprise.inject.spi.configurator.AnnotatedConstructorConfigurator;
+import jakarta.enterprise.inject.spi.configurator.AnnotatedFieldConfigurator;
+import jakarta.enterprise.inject.spi.configurator.AnnotatedMethodConfigurator;
+import
jakarta.enterprise.inject.spi.configurator.AnnotatedParameterConfigurator;
+import jakarta.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 15c31361a..0a11958ab 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 jakarta.enterprise.inject.spi.AnnotatedType;
import jakarta.enterprise.inject.spi.InterceptionFactory;
-import jakarta.enterprise.inject.spi.Interceptor;
import jakarta.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,41 @@ 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.getCachedProxyClass(interceptorInfo,
newAnnotatedType, classLoader);
+ final var classLoader =
ofNullable(originalInstance.getClass().getClassLoader())
+ .orElseGet(WebBeansUtil::getCurrentClassLoader);
- Map<Interceptor<?>,Object> interceptorInstances =
context.getInterceptorResolutionService()
- .createInterceptorInstances(interceptorInfo,
creationalContext);
+ var newAnnotatedType = configurator == null ? at :
configurator.getNewAnnotatedType();
+ newAnnotatedType.getTypeClosure(); // make sure the toString bellow is
accurate
+ var 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();
+ var interceptorResolutionService =
context.getInterceptorResolutionService();
+ var 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);
+ var interceptorInstances = interceptorResolutionService
+ .createInterceptorInstances(cache.interceptorInfo,
creationalContext);
+ var 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 +137,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)
+ {
+ var 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 6a68db8de..aab575921 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/proxy/InterceptorDecoratorProxyFactory.java
b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
index 8dfedfaea..8defa2279 100644
---
a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
+++
b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
@@ -67,7 +67,6 @@ public class InterceptorDecoratorProxyFactory extends
AbstractProxyFactory
* We need this to prevent filling up the ClassLoaders by
*/
private ConcurrentMap<Bean<?>, Class<?>> cachedProxyClasses = new
ConcurrentHashMap<>();
- private ConcurrentMap<AnnotatedType<?>, Class<?>> cachedProxyClassesByAt =
new ConcurrentHashMap<>();
public InterceptorDecoratorProxyFactory(WebBeansContext webBeansContext)
@@ -191,7 +190,6 @@ public class InterceptorDecoratorProxyFactory extends
AbstractProxyFactory
Class<T> proxyClass = createProxyClass(
classLoader, at.getJavaClass(),
intercepted.toArray(new Method[intercepted.size()]),
others.toArray(new Method[others.size()]));
- cachedProxyClassesByAt.put(at, proxyClass);
return proxyClass;
}
@@ -221,17 +219,6 @@ public class InterceptorDecoratorProxyFactory extends
AbstractProxyFactory
return clazz;
}
- public <T> Class<T>
getCachedProxyClass(InterceptorResolutionService.BeanInterceptorInfo
interceptorInfo,
- AnnotatedType<T> at, ClassLoader
classLoader)
- {
- Class<T> value = (Class<T>) cachedProxyClassesByAt.get(at);
- if (value == null)
- {
- value = createProxyClass(interceptorInfo, at, classLoader);
- }
- return value;
- }
-
public <T> Class<T> getCachedProxyClass(Bean<T> bean)
{
return (Class<T>) cachedProxyClasses.get(bean);
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 911b6848a..25c92c2fa 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
@@ -52,6 +52,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;
@@ -168,6 +169,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;
@@ -1747,6 +1750,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..9361fce44
--- /dev/null
+++
b/webbeans-impl/src/test/java/org/apache/webbeans/configurator/TrackingAnnotatedTypeConfiguratorImplTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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 jakarta.enterprise.inject.spi.*;
+import jakarta.enterprise.inject.spi.configurator.*;
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.portable.AnnotatedTypeImpl;
+import org.apache.webbeans.test.AbstractUnitTest;
+import org.apache.webbeans.util.WebBeansUtil;
+import org.junit.Test;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.*;
+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() {
+ return Set.of(new FullStubFieldConfigurator<>());
+ }
+
+ @Override
+ public Set<AnnotatedMethodConfigurator<? super MyBean>> methods() {
+ return Set.of(new FullStubMethodConfigurator<>());
+ }
+
+ @Override
+ public Set<AnnotatedConstructorConfigurator<MyBean>> constructors() {
+ return Set.of(new FullStubConstructorConfigurator<>());
+ }
+
+ @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()
{
+ return List.of(new FullStubParamConfigurator<>());
+ }
+ @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()
{
+ return List.of(new FullStubParamConfigurator<>());
+ }
+ @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 dd6f8d6ff..b875aa28c 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
@@ -18,38 +18,51 @@
*/
package org.apache.webbeans.test.interceptors.factory;
+import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.spi.CreationalContext;
+import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.inject.spi.InjectionPoint;
-import java.lang.annotation.Annotation;
-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 jakarta.enterprise.inject.spi.InterceptionFactory;
+import jakarta.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}
@@ -57,6 +70,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 var simpleQualifier = new
EmptyAnnotationLiteral<SimpleQualifier>(){};
+ final var r11 = getInstance(Runnable.class);
+ final var r12 = getInstance(Runnable.class);
+ assertEquals(1,
getWebBeansContext().getWebBeansUtil().getInterceptionFactoryCache().size());
+ final var 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
{
@@ -245,4 +287,12 @@ public class InterceptorDecoratorProxyFactoryTest extends
AbstractUnitTest
}
}
}
+
+ @Target({ METHOD })
+ @Retention(RUNTIME)
+ @Documented
+ @Qualifier
+ public @interface SimpleQualifier
+ {
+ }
}