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

sseifert pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-impl.git


The following commit(s) were added to refs/heads/master by this push:
     new 6cbc8a4  SLING-12472 Sling Models Impl: Implementation Picker service 
ranking is inversed (#60)
6cbc8a4 is described below

commit 6cbc8a4425930ae62586e84ff442bcec79d6e552
Author: Stefan Seifert <[email protected]>
AuthorDate: Tue Nov 12 12:17:15 2024 +0100

    SLING-12472 Sling Models Impl: Implementation Picker service ranking is 
inversed (#60)
---
 .../sling/models/impl/AdapterImplementations.java  |   6 +-
 .../sling/models/impl/ModelAdapterFactory.java     |  10 +-
 .../models/impl/AdapterImplementationsTest.java    |   4 +-
 .../sling/models/impl/ImplementsExtendsTest.java   |   2 +-
 ...apterFactory_ImplementationPickerOrderTest.java | 114 ++++++++++++++++++++
 .../ModelAdapterFactory_InjectorOrderTest.java     | 117 +++++++++++++++++++++
 .../sling/models/impl/MultipleInjectorTest.java    |   2 +-
 7 files changed, 246 insertions(+), 9 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/models/impl/AdapterImplementations.java 
b/src/main/java/org/apache/sling/models/impl/AdapterImplementations.java
index c454bae..a992d69 100644
--- a/src/main/java/org/apache/sling/models/impl/AdapterImplementations.java
+++ b/src/main/java/org/apache/sling/models/impl/AdapterImplementations.java
@@ -212,7 +212,11 @@ final class AdapterImplementations {
             implementationsArray[i] = implementationWrappersArray[i].getType();
         }
 
-        for (ImplementationPicker picker : sortedImplementationPickers) {
+        // find first-matching implementation (look in service ranking REVERSE 
order)
+        ImplementationPicker[] localPickers =
+                sortedImplementationPickers.toArray(new 
ImplementationPicker[sortedImplementationPickers.size()]);
+        for (int pickerIndex = localPickers.length - 1; pickerIndex >= 0; 
pickerIndex--) {
+            ImplementationPicker picker = localPickers[pickerIndex];
             Class<?> implementation = picker.pick(adapterType, 
implementationsArray, adaptable);
             if (implementation != null) {
                 for (int i = 0; i < implementationWrappersArray.length; i++) {
diff --git 
a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java 
b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
index 69a039b..cbcf41a 100644
--- a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
+++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
@@ -572,10 +572,12 @@ public class ModelAdapterFactory implements 
AdapterFactory, Runnable, ModelFacto
             if (StringUtils.isEmpty(source)) {
                 source = null;
             }
-            // find the right injector
-            final List<Injector> localInjectors = this.injectors;
+            // find the right injector (look in service ranking REVERSE order)
+            final Injector[] localInjectors = this.injectors.toArray(new 
Injector[this.injectors.size()]);
             boolean foundSource = false;
-            for (final Injector injector : localInjectors) {
+            for (int injectorIndex = localInjectors.length - 1; injectorIndex 
>= 0; injectorIndex--) {
+                final Injector injector = localInjectors[injectorIndex];
+
                 // if a source is given only use injectors with this name.
                 if (source != null && !source.equals(injector.getName())) {
                     continue;
@@ -1231,7 +1233,7 @@ public class ModelAdapterFactory implements 
AdapterFactory, Runnable, ModelFacto
         synchronized (this) {
             final Map<Comparable<?>, StaticInjectAnnotationProcessorFactory> 
factoryMap =
                     new 
TreeMap<>(this.staticInjectAnnotationProcessorFactories);
-            factoryMap.remove((Comparable<?>) props);
+            factoryMap.remove(props);
             this.staticInjectAnnotationProcessorFactories = factoryMap;
             
this.adapterImplementations.setStaticInjectAnnotationProcessorFactories(
                     staticInjectAnnotationProcessorFactories.values());
diff --git 
a/src/test/java/org/apache/sling/models/impl/AdapterImplementationsTest.java 
b/src/test/java/org/apache/sling/models/impl/AdapterImplementationsTest.java
index 7d068d0..0fd8b67 100644
--- a/src/test/java/org/apache/sling/models/impl/AdapterImplementationsTest.java
+++ b/src/test/java/org/apache/sling/models/impl/AdapterImplementationsTest.java
@@ -136,9 +136,9 @@ public class AdapterImplementationsTest {
                                 SAMPLE_ADAPTER,
                                 SAMPLE_ADAPTABLE,
                                 Arrays.asList(
-                                        new NoneImplementationPicker(),
+                                        new FirstImplementationPicker(),
                                         new LastImplementationPicker(),
-                                        new FirstImplementationPicker()))
+                                        new NoneImplementationPicker()))
                         .getType());
     }
 
diff --git 
a/src/test/java/org/apache/sling/models/impl/ImplementsExtendsTest.java 
b/src/test/java/org/apache/sling/models/impl/ImplementsExtendsTest.java
index 1b80283..9d09897 100644
--- a/src/test/java/org/apache/sling/models/impl/ImplementsExtendsTest.java
+++ b/src/test/java/org/apache/sling/models/impl/ImplementsExtendsTest.java
@@ -248,7 +248,7 @@ public class ImplementsExtendsTest {
     @Test
     public void testImplementsInterfaceModelWithPickLastImplementationPicker() 
{
         factory.implementationPickers =
-                Arrays.asList(new 
AdapterImplementationsTest.LastImplementationPicker(), 
firstImplementationPicker);
+                Arrays.asList(firstImplementationPicker, new 
AdapterImplementationsTest.LastImplementationPicker());
 
         Resource res = getMockResourceWithProps();
         SampleServiceInterface model = factory.getAdapter(res, 
SampleServiceInterface.class);
diff --git 
a/src/test/java/org/apache/sling/models/impl/ModelAdapterFactory_ImplementationPickerOrderTest.java
 
b/src/test/java/org/apache/sling/models/impl/ModelAdapterFactory_ImplementationPickerOrderTest.java
new file mode 100644
index 0000000..3818f37
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/models/impl/ModelAdapterFactory_ImplementationPickerOrderTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.sling.models.impl;
+
+import java.util.function.IntSupplier;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.adapter.AdapterManager;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.spi.ImplementationPicker;
+import org.apache.sling.models.testutil.ModelAdapterFactoryUtil;
+import org.apache.sling.scripting.api.BindingsValuesProvidersByContext;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.junit.Assert.assertEquals;
+import static org.osgi.framework.Constants.SERVICE_RANKING;
+
+/**
+ * Tests in which order the implementation pickers are handled depending on 
service ranking.
+ * For historic/backwards compatibility reasons, higher ranking value means 
lower priority (inverse to DS behavior).
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ModelAdapterFactory_ImplementationPickerOrderTest {
+
+    @Rule
+    public final OsgiContext context = new OsgiContext();
+
+    @Mock
+    private AdapterManager adapterManager;
+
+    @Mock
+    private BindingsValuesProvidersByContext bindingsValuesProvidersByContext;
+
+    @Mock
+    private SlingHttpServletRequest request;
+
+    private ModelAdapterFactory factory;
+
+    @Before
+    public void setUp() {
+        context.registerService(BindingsValuesProvidersByContext.class, 
bindingsValuesProvidersByContext);
+        context.registerService(AdapterManager.class, adapterManager);
+        factory = 
context.registerInjectActivateService(ModelAdapterFactory.class);
+
+        ModelAdapterFactoryUtil.addModelsForPackage(context.bundleContext(), 
Model1.class, Model2.class);
+    }
+
+    @Test
+    public void testFirstImplementationPicker() {
+        context.registerService(
+                ImplementationPicker.class, new FirstImplementationPicker(), 
SERVICE_RANKING, Integer.MAX_VALUE);
+
+        IntSupplier result = factory.createModel(request, IntSupplier.class);
+        assertEquals(1, result.getAsInt());
+    }
+
+    @Test
+    public void testMultipleImplementationPickers() {
+        // LastImplementationPicker has higher priority
+        context.registerService(
+                ImplementationPicker.class, new FirstImplementationPicker(), 
SERVICE_RANKING, Integer.MAX_VALUE);
+        context.registerService(ImplementationPicker.class, new 
LastImplementationPicker(), SERVICE_RANKING, 100);
+
+        IntSupplier result = factory.createModel(request, IntSupplier.class);
+        assertEquals(2, result.getAsInt());
+    }
+
+    static final class LastImplementationPicker implements 
ImplementationPicker {
+        @Override
+        public Class<?> pick(
+                @NotNull Class<?> adapterType, Class<?>[] 
implementationsTypes, @NotNull Object adaptable) {
+            return implementationsTypes[implementationsTypes.length - 1];
+        }
+    }
+
+    @Model(adaptables = SlingHttpServletRequest.class, adapters = 
IntSupplier.class)
+    static final class Model1 implements IntSupplier {
+        @Override
+        public int getAsInt() {
+            return 1;
+        }
+    }
+
+    @Model(adaptables = SlingHttpServletRequest.class, adapters = 
IntSupplier.class)
+    static final class Model2 implements IntSupplier {
+        @Override
+        public int getAsInt() {
+            return 2;
+        }
+    }
+}
diff --git 
a/src/test/java/org/apache/sling/models/impl/ModelAdapterFactory_InjectorOrderTest.java
 
b/src/test/java/org/apache/sling/models/impl/ModelAdapterFactory_InjectorOrderTest.java
new file mode 100644
index 0000000..63cf71c
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/models/impl/ModelAdapterFactory_InjectorOrderTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.sling.models.impl;
+
+import javax.inject.Inject;
+
+import java.util.Map;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.adapter.AdapterManager;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.impl.injectors.RequestAttributeInjector;
+import org.apache.sling.models.impl.injectors.ValueMapInjector;
+import org.apache.sling.models.spi.Injector;
+import org.apache.sling.models.testutil.ModelAdapterFactoryUtil;
+import org.apache.sling.scripting.api.BindingsValuesProvidersByContext;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+import static org.osgi.framework.Constants.SERVICE_RANKING;
+
+/**
+ * Tests in which order the injectors are handled depending on service ranking.
+ * For historic/backwards compatibility reasons, higher ranking value means 
lower priority (inverse to DS behavior).
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ModelAdapterFactory_InjectorOrderTest {
+
+    @Rule
+    public final OsgiContext context = new OsgiContext();
+
+    @Mock
+    private AdapterManager adapterManager;
+
+    @Mock
+    private BindingsValuesProvidersByContext bindingsValuesProvidersByContext;
+
+    @Mock
+    private SlingHttpServletRequest request;
+
+    @Mock
+    private Resource resource;
+
+    private ModelAdapterFactory factory;
+
+    @SuppressWarnings("null")
+    @Before
+    public void setUp() {
+        context.registerService(BindingsValuesProvidersByContext.class, 
bindingsValuesProvidersByContext);
+        context.registerService(AdapterManager.class, adapterManager);
+        factory = 
context.registerInjectActivateService(ModelAdapterFactory.class);
+
+        ModelAdapterFactoryUtil.addModelsForPackage(context.bundleContext(), 
TestModel.class);
+
+        when(request.getResource()).thenReturn(resource);
+        when(resource.adaptTo(ValueMap.class)).thenReturn(new 
ValueMapDecorator(Map.of("prop1", 1)));
+        when(request.getAttribute("prop1")).thenReturn(2);
+    }
+
+    @Test
+    public void testSingleInjector_ValueMap() {
+        context.registerService(Injector.class, new ValueMapInjector(), 
SERVICE_RANKING, 2000);
+
+        TestModel model = factory.createModel(request, TestModel.class);
+        assertEquals((Integer) 1, model.getProp1());
+    }
+
+    @Test
+    public void testSingleInjector_RequestAttribute() {
+        context.registerService(Injector.class, new 
RequestAttributeInjector(), SERVICE_RANKING, 4000);
+
+        TestModel model = factory.createModel(request, TestModel.class);
+        assertEquals((Integer) 2, model.getProp1());
+    }
+
+    @Test
+    public void testMultipleInjectors() {
+        // ValueMapInjector has higher priority
+        context.registerService(Injector.class, new 
RequestAttributeInjector(), SERVICE_RANKING, 4000);
+        context.registerService(Injector.class, new ValueMapInjector(), 
SERVICE_RANKING, 2000);
+
+        TestModel model = factory.createModel(request, TestModel.class);
+        assertEquals((Integer) 1, model.getProp1());
+    }
+
+    @Model(adaptables = SlingHttpServletRequest.class)
+    private interface TestModel {
+        @Inject
+        Integer getProp1();
+    }
+}
diff --git 
a/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java 
b/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java
index e7bc4ac..2b49009 100644
--- a/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java
+++ b/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java
@@ -64,7 +64,7 @@ public class MultipleInjectorTest {
 
         factory = AdapterFactoryTest.createModelAdapterFactory();
         // binding injector should be asked first as it has a lower service 
ranking!
-        factory.injectors = Arrays.asList(bindingsInjector, 
attributesInjector);
+        factory.injectors = Arrays.asList(attributesInjector, 
bindingsInjector);
         factory.bindStaticInjectAnnotationProcessorFactory(bindingsInjector, 
new ServicePropertiesMap(1, 1));
 
         
when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);

Reply via email to