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

adelbene pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git


The following commit(s) were added to refs/heads/master by this push:
     new 154373e5b2 WICKET-7170: AnnotProxyFieldValueFactory now considers 
defaultCandidate (#1311)
154373e5b2 is described below

commit 154373e5b2f5d5524596300d6dc031757e07b7a4
Author: Hans Schäfer <[email protected]>
AuthorDate: Thu Nov 27 21:59:34 2025 +0100

    WICKET-7170: AnnotProxyFieldValueFactory now considers defaultCandidate 
(#1311)
    
    * WICKET-7170: AnnotProxyFieldValueFactory now considers 
defaultCandidate-Flag
    
    * Reset tests to focus only on implementation / bugfix
    
    * Added test for DefaultCandidate-Scenario
    
    * Update 
wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * Update 
wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * Update 
wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * Update 
wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * Update 
wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * Update 
wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * Update 
wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * Update 
wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactoryDefaultCandidateTest.java
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * Update 
wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactoryDefaultCandidateTest.java
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    ---------
    
    Co-authored-by: Schäfer, H.H. (Hans Hosea) <[email protected]>
    Co-authored-by: Martin Grigorov <[email protected]>
---
 .../wicket/spring/ISpringContextLocator.java       |   1 +
 .../annot/AnnotProxyFieldValueFactory.java         |  90 ++++++++++-------
 .../wicket/spring/test/ApplicationContextMock.java |   4 +-
 ...ProxyFieldValueFactoryDefaultCandidateTest.java | 107 +++++++++++++++++++++
 4 files changed, 166 insertions(+), 36 deletions(-)

diff --git 
a/wicket-spring/src/main/java/org/apache/wicket/spring/ISpringContextLocator.java
 
b/wicket-spring/src/main/java/org/apache/wicket/spring/ISpringContextLocator.java
index 41de72ad69..86e43714d1 100644
--- 
a/wicket-spring/src/main/java/org/apache/wicket/spring/ISpringContextLocator.java
+++ 
b/wicket-spring/src/main/java/org/apache/wicket/spring/ISpringContextLocator.java
@@ -47,6 +47,7 @@ import org.springframework.context.ApplicationContext;
  * @author Igor Vaynberg (ivaynberg)
  * 
  */
+@FunctionalInterface
 public interface ISpringContextLocator extends IClusterable
 {
        /**
diff --git 
a/wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
 
b/wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
index 692857f86c..5816d198ea 100644
--- 
a/wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
+++ 
b/wicket-spring/src/main/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactory.java
@@ -22,7 +22,9 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.ConcurrentMap;
+import java.util.function.Predicate;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Named;
@@ -132,7 +134,7 @@ public class AnnotProxyFieldValueFactory implements 
IFieldValueFactory
                        }
 
                        ResolvableType resolvableType = 
ResolvableType.forField(field);
-                       String beanName = getBeanName(field, name, required, 
resolvableType);
+                       String beanName = getBeanName(field, name, 
resolvableType);
 
                        SpringBeanLocator locator = new 
SpringBeanLocator(beanName, field.getType(), field, contextLocator);
 
@@ -181,11 +183,11 @@ public class AnnotProxyFieldValueFactory implements 
IFieldValueFactory
        }
 
        /**
-        * 
+        *
         * @param field
         * @return bean name
         */
-       private String getBeanName(final Field field, String name, boolean 
required, ResolvableType resolvableType)
+       private String getBeanName(final Field field, String name, final 
ResolvableType resolvableType)
        {
                if (Strings.isEmpty(name))
                {
@@ -230,14 +232,14 @@ public class AnnotProxyFieldValueFactory implements 
IFieldValueFactory
                        
Arrays.asList(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(ctx, 
resolvableType)));
 
                // filter out beans that are not candidates for autowiring
-               if (ctx instanceof AbstractApplicationContext)
+               if (ctx instanceof AbstractApplicationContext 
abstractApplicationContext)
                {
-                       Iterator<String> it = names.iterator();
+                       final Iterator<String> it = names.iterator();
                        while (it.hasNext())
                        {
                                final String possibility = it.next();
-                               BeanDefinition beanDef = getBeanDefinition(
-                                       
((AbstractApplicationContext)ctx).getBeanFactory(), possibility);
+                               final ConfigurableListableBeanFactory 
beanFactory = abstractApplicationContext.getBeanFactory();
+                               final BeanDefinition beanDef = 
getBeanDefinition(beanFactory, possibility);
                                if 
(BeanFactoryUtils.isFactoryDereference(possibility) ||
                                        possibility.startsWith("scopedTarget.") 
||
                                        (beanDef != null && 
!beanDef.isAutowireCandidate()))
@@ -249,36 +251,22 @@ public class AnnotProxyFieldValueFactory implements 
IFieldValueFactory
 
                if (names.size() > 1)
                {
-                       if (ctx instanceof AbstractApplicationContext)
-                       {
-                               List<String> primaries = new ArrayList<>();
-                               for (String name : names)
-                               {
-                                       BeanDefinition beanDef = 
getBeanDefinition(
-                                               
((AbstractApplicationContext)ctx).getBeanFactory(), name);
-                                       if (beanDef instanceof 
AbstractBeanDefinition)
-                                       {
-                                               if (beanDef.isPrimary())
-                                               {
-                                                       primaries.add(name);
-                                               }
-                                       }
-                               }
-                               if (primaries.size() == 1)
-                               {
-                                       return primaries.get(0);
-                               }
-                       }
-                       
-                       //use field name to find a match
-                       int nameIndex = names.indexOf(fieldName);
-                       
-                       if (nameIndex > -1)
+                       // Check, if we can reduce the set of beannames to 
exactly one beanname probing the following criterias:
+                       // 1. Is there exactly one bean marked as primary?
+                       // 2. Is there a bean with the same name as the field?
+                       // 3. Is there exactly one bean marked as default 
candidate?
+                       final String exactMatchBeanName = 
Optional.ofNullable(detectPrimaryBeanName(ctx, names))
+                         .or(() -> 
Optional.ofNullable(detectBeanNameByFieldName(fieldName, names)))
+                         .orElseGet(() -> detectDefaultCandidateBeanName(ctx, 
names));
+
+                       // If so: take that beanname
+                       if (exactMatchBeanName != null)
                        {
-                               return names.get(nameIndex);
+                               return exactMatchBeanName;
                        }
 
-                       StringBuilder msg = new StringBuilder();
+                       // Hmm, dont know which one to take....
+                       final StringBuilder msg = new StringBuilder();
                        msg.append("More than one bean of type [");
                        msg.append(clazz.getName());
                        msg.append("] found, you have to specify the name of 
the bean ");
@@ -295,6 +283,40 @@ public class AnnotProxyFieldValueFactory implements 
IFieldValueFactory
                return null;
        }
 
+       private String detectPrimaryBeanName(final ApplicationContext ctx, 
final List<String> beanNames)
+       {
+               return detectBeanName(ctx, beanNames, 
AbstractBeanDefinition::isPrimary);
+       }
+
+       private String detectDefaultCandidateBeanName(final ApplicationContext 
ctx, final List<String> beanNames)
+       {
+               return detectBeanName(ctx, beanNames, 
AbstractBeanDefinition::isDefaultCandidate);
+       }
+
+       private String detectBeanName(final ApplicationContext ctx, final 
List<String> beanNames, final Predicate<AbstractBeanDefinition> predicate)
+       {
+               final List<String> found = new ArrayList<>();
+               if (ctx instanceof AbstractApplicationContext 
abstractApplicationContext)
+               {
+                       final ConfigurableListableBeanFactory beanFactory = 
abstractApplicationContext.getBeanFactory();
+                       for (final String beanName : beanNames)
+                       {
+                               final BeanDefinition beanDefinition = 
getBeanDefinition(beanFactory, beanName);
+                               if (beanDefinition instanceof 
AbstractBeanDefinition abstractBeanDefinition && 
predicate.test(abstractBeanDefinition))
+                               {
+                                       found.add(beanName);
+                               }
+                       }
+               }
+               return found.size() == 1 ? found.get(0) : null;
+       }
+
+       private String detectBeanNameByFieldName(final String fieldName, final 
List<String> beanNames) 
+       {
+               return fieldName != null && beanNames.contains(fieldName) ? 
fieldName : null;
+       }
+
+
        public BeanDefinition getBeanDefinition(final 
ConfigurableListableBeanFactory beanFactory,
                final String name)
        {
diff --git 
a/wicket-spring/src/main/java/org/apache/wicket/spring/test/ApplicationContextMock.java
 
b/wicket-spring/src/main/java/org/apache/wicket/spring/test/ApplicationContextMock.java
index d5a83f8e64..5be624c52b 100644
--- 
a/wicket-spring/src/main/java/org/apache/wicket/spring/test/ApplicationContextMock.java
+++ 
b/wicket-spring/src/main/java/org/apache/wicket/spring/test/ApplicationContextMock.java
@@ -64,9 +64,9 @@ public class ApplicationContextMock extends 
AbstractApplicationContext implement
         * @param name
         * @param bean
         */
-       public <T extends Object> void putBean(final String name, final T bean)
+       public <T> void putBean(final String name, final T bean)
        {
-               beanFactory.registerBeanDefinition(name, new 
RootBeanDefinition((Class<T>)bean.getClass(), () -> bean));
+               beanFactory.registerBeanDefinition(name, new 
RootBeanDefinition((Class<T>) bean.getClass(), () -> bean));
        }
 
        /**
diff --git 
a/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactoryDefaultCandidateTest.java
 
b/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactoryDefaultCandidateTest.java
new file mode 100644
index 0000000000..264095089c
--- /dev/null
+++ 
b/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/AnnotProxyFieldValueFactoryDefaultCandidateTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.wicket.spring.injection.annot;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import java.lang.reflect.Field;
+import java.util.stream.Stream;
+
+import org.apache.wicket.proxy.ILazyInitProxy;
+import org.apache.wicket.proxy.IProxyTargetLocator;
+import org.apache.wicket.spring.test.ApplicationContextMock;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+
+import jakarta.inject.Inject;
+
+/**
+ * Tests for AnnotProxyFieldValueFactory
+ * https://issues.apache.org/jira/browse/WICKET-7170
+ * 
+ * @author hosea
+ */
+public class AnnotProxyFieldValueFactoryDefaultCandidateTest
+{
+
+       @ParameterizedTest
+       @MethodSource("beans")
+       public void 
shouldCreateProxyForUniqueDefaultCandidate_beanNameAmbiguous(final Object obj) 
throws Exception {
+               final Bean defaultCandidate = new Bean();
+               final ApplicationContextMock applicationContext = new 
ApplicationContextMock();
+               // add two beans to make ambiguous
+               applicationContext.putBean("anyBean", new Bean());
+               applicationContext.putBean("primaryBean", defaultCandidate);
+               final AbstractBeanDefinition abstractBeanDefinition = 
(AbstractBeanDefinition) 
applicationContext.getBeanFactory().getBeanDefinition("anyBean");
+               abstractBeanDefinition.setDefaultCandidate(false);
+               final AnnotProxyFieldValueFactory factory = new 
AnnotProxyFieldValueFactory(() -> applicationContext);
+
+               final Field beanByClassField = 
obj.getClass().getDeclaredField("beanByClass");
+               final Object beanByClassProxy = 
factory.getFieldValue(beanByClassField, obj);
+               final ILazyInitProxy lazyInitProxy = 
assertInstanceOf(ILazyInitProxy.class, beanByClassProxy);
+               final IProxyTargetLocator beanByClassLocator = 
lazyInitProxy.getObjectLocator();
+               assertSame(defaultCandidate, 
beanByClassLocator.locateProxyTarget());
+       }
+
+
+       private static Stream<Object> beans() {
+               return Stream.of(new SpringBeanInjectable(), new 
JakartaInjectInjectable());
+       }
+
+       /**
+        * Test creation fails with null springcontextlocator
+        */
+       @Test
+       public void testNullContextLocator()
+       {
+               Assertions.assertThrows(IllegalArgumentException.class, () -> 
new AnnotProxyFieldValueFactory(null));
+       }
+
+
+       public static class JakartaInjectInjectable
+       {
+               @Inject
+               private Bean beanByClass;
+
+               @Override
+               public String toString() {
+                       return "JakartaInjectInjectable";
+               }
+       }
+
+       public static class SpringBeanInjectable
+       {
+               @SpringBean
+               private Bean beanByClass;
+
+               @Override
+               public String toString() {
+                       return "SpringBeanInjectable";
+               }
+       }
+
+       /**
+        * Mock spring bean
+        */
+       public static class Bean
+       {
+       }
+}

Reply via email to