This is an automated email from the ASF dual-hosted git repository.
tandraschko 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 593e02b34 OWB-1456 - [perf] optimize
configureProducerMethodSpecializations
593e02b34 is described below
commit 593e02b34e08e8f32a397f6522a9b33cd31391d6
Author: tandraschko <[email protected]>
AuthorDate: Tue Apr 14 16:53:04 2026 +0200
OWB-1456 - [perf] optimize configureProducerMethodSpecializations
---
.../org/apache/webbeans/config/BeansDeployer.java | 81 +++++++++-------
.../ProducerMethodSpecializationOverloadTest.java | 106 +++++++++++++++++++++
2 files changed, 151 insertions(+), 36 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 bde8afaac..1b9f88d85 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
@@ -117,6 +117,7 @@ import java.lang.reflect.Type;
import java.net.URL;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -129,8 +130,6 @@ import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
import static java.util.Arrays.asList;
import static org.apache.webbeans.spi.BeanArchiveService.BeanDiscoveryMode;
import static
org.apache.webbeans.spi.BeanArchiveService.BeanArchiveInformation;
@@ -1047,53 +1046,63 @@ public class BeansDeployer
{
Set<Bean<?>> beans = webBeansContext.getBeanManagerImpl().getBeans();
- // Collect all producer method beans
- // and sort them with subclasses first
- // This is important as we can later go top-down and
- // disable all 'overwritten' producer methods which must be
- // further down in the list
- List<ProducerMethodBean> producerMethodBeans = beans.stream()
- .filter(ProducerMethodBean.class::isInstance)
- .map(ProducerMethodBean.class::cast)
- .collect(Collectors.toList());
-
- checkSpecializedProducerMethodConditions(producerMethodBeans);
-
- for (int i = 0; i < producerMethodBeans.size(); i++)
+ List<ProducerMethodBean<?>> producerMethodBeans = new ArrayList<>();
+ Map<String, List<ProducerMethodBean<?>>> byCreatorMethodName = new
HashMap<>();
+ for (Bean<?> bean : beans)
{
- ProducerMethodBean<?> producerMethodBean =
producerMethodBeans.get(i);
- if (!producerMethodBean.isEnabled())
+ if (!(bean instanceof ProducerMethodBean))
{
continue;
}
+ ProducerMethodBean<?> pmb = (ProducerMethodBean<?>) bean;
+ producerMethodBeans.add(pmb);
+ byCreatorMethodName
+ .computeIfAbsent(pmb.getCreatorMethod().getName(), k ->
new ArrayList<>())
+ .add(pmb);
+ }
- for (int j = 0; j < producerMethodBeans.size(); j++)
- {
- ProducerMethodBean<?> otherProducerMethodBean =
producerMethodBeans.get(j);
+ checkSpecializedProducerMethodConditions(producerMethodBeans);
- if (i==j)
+ // Only producers sharing the same Java method name can specialize
each other; compare within
+ // those buckets (not full n² over all producer methods). Disabling
still requires the same
+ // erased parameter types as overriding / CDI producer specialization
(so unrelated overloads stay).
+ for (List<ProducerMethodBean<?>> sameName :
byCreatorMethodName.values())
+ {
+ int size = sameName.size();
+ for (int i = 0; i < size; i++)
+ {
+ ProducerMethodBean<?> producerMethodBean = sameName.get(i);
+ if (!producerMethodBean.isEnabled())
{
- // makes no sense to compare with ourselves
continue;
}
- if (!otherProducerMethodBean.isEnabled())
+ for (int j = 0; j < size; j++)
{
- // already disabled
- continue;
- }
+ ProducerMethodBean<?> otherProducerMethodBean =
sameName.get(j);
- if
(producerMethodBean.getBeanClass().equals(otherProducerMethodBean.getBeanClass()))
- {
- // must be another producerMethod from the same class.
Probably different qualifier?
- continue;
- }
+ if (i == j)
+ {
+ // makes no sense to compare with ourselves
+ continue;
+ }
- if
(otherProducerMethodBean.getBeanClass().isAssignableFrom(producerMethodBean.getBeanClass()))
- {
- // yikes we did hit a superclass!
+ if (!otherProducerMethodBean.isEnabled())
+ {
+ // already disabled
+ continue;
+ }
+
+ if
(producerMethodBean.getBeanClass().equals(otherProducerMethodBean.getBeanClass()))
+ {
+ // must be another producerMethod from the same class.
Probably different qualifier?
+ continue;
+ }
- if
(producerMethodBean.getCreatorMethod().getName().equals(otherProducerMethodBean.getCreatorMethod().getName()))
+ if
(otherProducerMethodBean.getBeanClass().isAssignableFrom(producerMethodBean.getBeanClass())
+ && Arrays.equals(
+
producerMethodBean.getCreatorMethod().getParameterTypes(),
+
otherProducerMethodBean.getCreatorMethod().getParameterTypes()))
{
// disable the other bean as it got 'specialized' with
the current bean.
otherProducerMethodBean.setEnabled(false);
@@ -1107,7 +1116,7 @@ public class BeansDeployer
* Verify that all conditions for Specialized producer methdods are met.
* See spec section 3.3.3. Specializing a producer method
*/
- private void
checkSpecializedProducerMethodConditions(List<ProducerMethodBean> producerBeans)
+ private void
checkSpecializedProducerMethodConditions(List<ProducerMethodBean<?>>
producerBeans)
{
Set<String> methodsDisabledDueToSpecialization = new HashSet<>();
for (ProducerMethodBean producerBean : producerBeans)
diff --git
a/webbeans-impl/src/test/java/org/apache/webbeans/test/specalization/ProducerMethodSpecializationOverloadTest.java
b/webbeans-impl/src/test/java/org/apache/webbeans/test/specalization/ProducerMethodSpecializationOverloadTest.java
new file mode 100644
index 000000000..642c8c155
--- /dev/null
+++
b/webbeans-impl/src/test/java/org/apache/webbeans/test/specalization/ProducerMethodSpecializationOverloadTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.test.specalization;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Set;
+
+import jakarta.enterprise.inject.Produces;
+import jakarta.enterprise.inject.Specializes;
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.util.AnnotationLiteral;
+import jakarta.inject.Qualifier;
+
+import org.apache.webbeans.test.AbstractUnitTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * A subclass {@code @Specializes} producer must not disable a superclass
producer that only shares
+ * the same Java method name but has a different parameter list (CDI 3.3.3 /
Java overriding).
+ */
+public class ProducerMethodSpecializationOverloadTest extends AbstractUnitTest
+{
+ @Test
+ public void testSpecializingNoArgDoesNotDisableParameterOverload()
+ {
+ startContainer(BaseOverload.class, SubSpecializesNoArgOnly.class,
ProducerDep.class);
+
+ Set<Bean<?>> noArg = getBeanManager().getBeans(String.class, new
AnnotationLiteral<QNoArg>()
+ {
+ });
+ Assert.assertEquals(1, noArg.size());
+
+ Set<Bean<?>> depOverload = getBeanManager().getBeans(String.class, new
AnnotationLiteral<QDepOverload>()
+ {
+ });
+ Assert.assertEquals("overloaded producer (same name, different
parameters) must remain enabled", 1, depOverload.size());
+ Assert.assertEquals(BaseOverload.class,
depOverload.iterator().next().getBeanClass());
+ }
+
+ public static class BaseOverload
+ {
+ @Produces
+ @QNoArg
+ public String m()
+ {
+ return "base-m";
+ }
+
+ @Produces
+ @QDepOverload
+ public String m(ProducerDep ignored)
+ {
+ return "base-m-dep";
+ }
+ }
+
+ /** Parameter type for the overloaded producer. */
+ public static class ProducerDep
+ {
+ }
+
+ public static class SubSpecializesNoArgOnly extends BaseOverload
+ {
+ @Produces
+ @Specializes
+ @QNoArg
+ public String m()
+ {
+ return "sub-m";
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER,
ElementType.TYPE})
+ @Qualifier
+ public @interface QNoArg
+ {
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER,
ElementType.TYPE})
+ @Qualifier
+ public @interface QDepOverload
+ {
+ }
+}