You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ss...@apache.org on 2021/12/22 22:34:26 UTC

[sling-org-apache-sling-models-caconfig] branch master updated: SLING-7256 Sling Models injector for CAConfig (#1)

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-caconfig.git


The following commit(s) were added to refs/heads/master by this push:
     new fcff806  SLING-7256 Sling Models injector for CAConfig (#1)
fcff806 is described below

commit fcff806e6aab1f791a5a52cd55997a851bb435ce
Author: Stefan Seifert <st...@users.noreply.github.com>
AuthorDate: Wed Dec 22 23:34:23 2021 +0100

    SLING-7256 Sling Models injector for CAConfig (#1)
---
 pom.xml                                            |  63 ++++--
 .../annotations/ContextAwareConfiguration.java     |  57 +++++
 .../caconfig/{ => annotations}/package-info.java   |   2 +-
 .../ContextAwareConfigurationInjector.java         | 196 +++++++++++++++++
 .../ContextAwareConfigurationProcessor.java}       |  21 +-
 .../caconfig/example/caconfig/ListConfig.java}     |  15 +-
 .../caconfig/example/caconfig/SingleConfig.java}   |  15 +-
 .../caconfig/model/ConfigurationValuesModel.java   |  58 +++++
 .../example/model/InvalidAnnotationListModel.java} |  22 +-
 .../example/model/InvalidAnnotationModel.java}     |  20 +-
 .../example/model/InvalidInjectModel.java}         |  24 +-
 .../caconfig/example/model/InvalidSetModel.java}   |  23 +-
 .../example/model/ListConfigAdaptModel.java        |  57 +++++
 .../caconfig/example/model/ListConfigGetter.java}  |  15 +-
 .../caconfig/example/model/ListConfigModel.java    |  57 +++++
 .../example/model/ListConfigValueMapModel.java     |  57 +++++
 .../example/model/SingleConfigAdaptModel.java}     |  21 +-
 .../example/model/SingleConfigGetter.java}         |   8 +-
 .../caconfig/example/model/SingleConfigModel.java} |  22 +-
 .../example/model/SingleConfigValueMapModel.java}  |  21 +-
 .../ContextAwareConfigurationInjectorTest.java     | 243 +++++++++++++++++++++
 21 files changed, 959 insertions(+), 58 deletions(-)

diff --git a/pom.xml b/pom.xml
index a5d6f19..8bbc630 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,30 +46,55 @@
     <dependencies>
 
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.annotation.versioning</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.metatype.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.16.4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.models.api</artifactId>
-            <version>1.3.0</version>
+            <version>1.3.8</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.caconfig.api</artifactId>
-            <version>1.0.0</version>
+            <version>1.1.2</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.annotation.versioning</artifactId>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.caconfig.spi</artifactId>
+            <version>1.4.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.service.component.annotations</artifactId>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.caconfig.impl</artifactId>
+            <version>1.6.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.service.metatype.annotations</artifactId>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.4</version>
             <scope>provided</scope>
         </dependency>
 
@@ -79,12 +104,6 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.api</artifactId>
-            <version>2.4.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <scope>provided</scope>
@@ -103,7 +122,7 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.testing.osgi-mock.junit5</artifactId>
+            <artifactId>org.apache.sling.testing.sling-mock.junit5</artifactId>
             <version>3.2.2</version>
             <scope>test</scope>
         </dependency>
@@ -114,14 +133,16 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <version>4.2.0</version>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.caconfig-mock-plugin</artifactId>
+            <version>1.4.0-SNAPSHOT</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-junit-jupiter</artifactId>
-            <version>4.2.0</version>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <version>1.3.2</version>
+            <scope>test</scope>
         </dependency>
 
     </dependencies>
diff --git a/src/main/java/org/apache/sling/models/caconfig/annotations/ContextAwareConfiguration.java b/src/main/java/org/apache/sling/models/caconfig/annotations/ContextAwareConfiguration.java
new file mode 100644
index 0000000..0c527b5
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/caconfig/annotations/ContextAwareConfiguration.java
@@ -0,0 +1,57 @@
+/*
+ * 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.caconfig.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.sling.models.annotations.Source;
+import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
+
+/**
+ * Annotation to be used on either methods, fields or constructor parameters to let Sling Models inject a context-aware configuration.
+ * The annotation supports the same features and semantics as {@link org.apache.sling.caconfig.ConfigurationBuilder}.
+ * For configuration collections, you can use arrays, {@link java.util.List} or {@link java.util.Collection} variables.
+ */
+@Target({ METHOD, FIELD, PARAMETER })
+@Retention(RUNTIME)
+@InjectAnnotation
+@Source("caconfig")
+public @interface ContextAwareConfiguration {
+
+    /**
+     * Define configuration name.
+     * Optional if used together with a context-aware configuration annotation class, which implictely defines a configuration name.
+     * @return Configuration name
+     */
+    public String name() default "";
+
+    /**
+     * if set to REQUIRED injection is mandatory, if set to OPTIONAL injection is optional, in case of DEFAULT
+     * the standard annotations ({@link org.apache.sling.models.annotations.Optional}, {@link org.apache.sling.models.annotations.Required}) are used.
+     * If even those are not available the default injection strategy defined on the {@link org.apache.sling.models.annotations.Model} applies.
+     * Default value = DEFAULT.
+     */
+    public InjectionStrategy injectionStrategy() default InjectionStrategy.DEFAULT;
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/main/java/org/apache/sling/models/caconfig/annotations/package-info.java
similarity index 89%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/main/java/org/apache/sling/models/caconfig/annotations/package-info.java
index b5cef1e..35cfc58 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/main/java/org/apache/sling/models/caconfig/annotations/package-info.java
@@ -18,6 +18,6 @@
  */
 
 @Version("1.0.0")
-package org.apache.sling.models.caconfig;
+package org.apache.sling.models.caconfig.annotations;
 
 import org.osgi.annotation.versioning.Version;
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationInjector.java b/src/main/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationInjector.java
new file mode 100644
index 0000000..ff28cbf
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationInjector.java
@@ -0,0 +1,196 @@
+/*
+ * 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.caconfig.impl.injectors;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Array;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.caconfig.ConfigurationBuilder;
+import org.apache.sling.caconfig.ConfigurationResolveException;
+import org.apache.sling.caconfig.ConfigurationResolver;
+import org.apache.sling.caconfig.annotation.Configuration;
+import org.apache.sling.caconfig.management.multiplexer.ConfigurationInjectResourceDetectionStrategyMultiplexer;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+import org.apache.sling.models.spi.AcceptsNullName;
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.Injector;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor2;
+import org.apache.sling.models.spi.injectorspecific.StaticInjectAnnotationProcessorFactory;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.propertytypes.ServiceRanking;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(service = { Injector.class, StaticInjectAnnotationProcessorFactory.class, AcceptsNullName.class })
+@ServiceRanking(6000)
+public class ContextAwareConfigurationInjector implements Injector, StaticInjectAnnotationProcessorFactory, AcceptsNullName {
+
+    private static final Logger log = LoggerFactory.getLogger(ContextAwareConfigurationInjector.class);
+
+    @Reference
+    private ConfigurationResolver configurationResolver;
+    @Reference
+    private ConfigurationInjectResourceDetectionStrategyMultiplexer configurationInjectResourceDetectionStrategyMultiplexer;
+
+    @Override
+    public @NotNull String getName() {
+        return "caconfig";
+    }
+
+    @Override
+    public InjectAnnotationProcessor2 createAnnotationProcessor(AnnotatedElement element) {
+        // check if the element has the expected annotation
+        ContextAwareConfiguration annotation = getAnnotation(element);
+        if (annotation != null) {
+            return new ContextAwareConfigurationProcessor(annotation);
+        }
+        return null;
+    }
+
+    @Override
+    public Object getValue(@NotNull Object adaptable, String name,
+            @NotNull Type declaredType, @NotNull AnnotatedElement element,
+            @NotNull DisposalCallbackRegistry callbackRegistry) {
+
+        ContextAwareConfiguration annotation = getAnnotation(element);
+        if (annotation == null) {
+            // support injections only with explicit @ContextAwareConfiguration annotation
+            log.warn("Injection only supported using @ContextAwareConfiguration annotation.");
+            return null;
+        }
+
+        // get resource
+        Resource resource = getResource(adaptable);
+        if (resource == null) {
+            log.warn("Unable to get resource from {}", adaptable);
+            return null;
+        }
+
+        // initialize configuration builder
+        ConfigurationBuilder configurationBuilder = configurationResolver.get(resource);
+        if (StringUtils.isNotBlank(annotation.name())) {
+            configurationBuilder = configurationBuilder.name(annotation.name());
+        }
+
+        // detect from declared type if a single configuration or configuration collection is requested and return the configuration
+        if (declaredType instanceof Class) {
+            Class<?> clazz = (Class<?>)declaredType;
+            if (clazz.isArray()) {
+                Collection<?> result = getConfigurationCollection(configurationBuilder, clazz.getComponentType());
+                Object array = Array.newInstance(clazz.getComponentType(), result.size());
+                Iterator<?> resultIterator = result.iterator();
+                int i = 0;
+                while (resultIterator.hasNext()) {
+                    Array.set(array, i++, resultIterator.next());
+                }
+                return array;
+            }
+            else {
+                return getConfiguration(configurationBuilder, clazz);
+            }
+        }
+        else if (declaredType instanceof ParameterizedType) {
+            ParameterizedType parameterizedType = (ParameterizedType)declaredType;
+            if (parameterizedType.getActualTypeArguments().length != 1) {
+                return null;
+            }
+            Class<?> collectionType = (Class<?>) parameterizedType.getRawType();
+            if (!(collectionType.equals(Collection.class) || collectionType.equals(List.class))) {
+                return null;
+            }
+            Class<?> clazz = (Class<?>) parameterizedType.getActualTypeArguments()[0];
+            Collection<?> result = getConfigurationCollection(configurationBuilder, clazz);
+            if (collectionType.equals(List.class)) {
+                return new ArrayList<>(result);
+            }
+            else {
+                return result;
+            }
+        }
+        else {
+            log.warn("Cannot handle type {}", declaredType);
+            return null;
+        }
+    }
+
+    private @Nullable ContextAwareConfiguration getAnnotation(AnnotatedElement element) {
+        return element.getAnnotation(ContextAwareConfiguration.class);
+    }
+
+    private @Nullable Resource getResource(@NotNull Object adaptable) {
+        if (adaptable instanceof Resource) {
+            return (Resource)adaptable;
+        }
+        if (adaptable instanceof SlingHttpServletRequest) {
+            SlingHttpServletRequest request = (SlingHttpServletRequest)adaptable;
+            Resource resource = configurationInjectResourceDetectionStrategyMultiplexer.detectResource(request);
+            if (resource == null) {
+                resource = request.getResource();
+            }
+            return resource;
+        }
+        return null;
+    }
+
+    private @Nullable Object getConfiguration(@NotNull ConfigurationBuilder configurationBuilder, @NotNull Class<?> clazz) {
+        try {
+            if (clazz.equals(ValueMap.class)) {
+                return configurationBuilder.asValueMap();
+            }
+            if (isContextAwareConfigAnnotationClass(clazz)) {
+                return configurationBuilder.as(clazz);
+            }
+            return configurationBuilder.asAdaptable(clazz);
+        }
+        catch (ConfigurationResolveException ex) {
+            throw new ConfigurationResolveException("Class " + clazz.getName() + ": " + ex.getMessage(), ex);
+        }
+    }
+
+    private @NotNull Collection<?> getConfigurationCollection(@NotNull ConfigurationBuilder configurationBuilder, @NotNull Class<?> clazz) {
+        try {
+            if (clazz.equals(ValueMap.class)) {
+                return configurationBuilder.asValueMapCollection();
+            }
+            if (isContextAwareConfigAnnotationClass(clazz)) {
+                return configurationBuilder.asCollection(clazz);
+            }
+            return configurationBuilder.asAdaptableCollection(clazz);
+        }
+        catch (ConfigurationResolveException ex) {
+            throw new ConfigurationResolveException("Class " + clazz.getName() + ": " + ex.getMessage(), ex);
+        }
+    }
+
+    private boolean isContextAwareConfigAnnotationClass(Class<?> clazz) {
+        return clazz.isAnnotation() && clazz.isAnnotationPresent(Configuration.class);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/main/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationProcessor.java
similarity index 53%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/main/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationProcessor.java
index b5cef1e..30bcec4 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/main/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationProcessor.java
@@ -16,8 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.impl.injectors;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor2;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+class ContextAwareConfigurationProcessor extends AbstractInjectAnnotationProcessor2 {
+
+    private final ContextAwareConfiguration annotation;
+
+    public ContextAwareConfigurationProcessor(ContextAwareConfiguration annotation) {
+        this.annotation = annotation;
+    }
+
+    @Override
+    public InjectionStrategy getInjectionStrategy() {
+        return annotation.injectionStrategy();
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/ListConfig.java
similarity index 75%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/caconfig/ListConfig.java
index b5cef1e..104419d 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/ListConfig.java
@@ -16,8 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.caconfig;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import org.apache.sling.caconfig.annotation.Configuration;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+@Configuration(collection = true)
+public @interface ListConfig {
+
+    String stringParam();
+
+    int intParam() default 5;
+
+    boolean boolParam();
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/SingleConfig.java
similarity index 74%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/caconfig/SingleConfig.java
index b5cef1e..61ee707 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/SingleConfig.java
@@ -16,8 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.caconfig;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import org.apache.sling.caconfig.annotation.Configuration;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+@Configuration(name = "testSingleConfig")
+public @interface SingleConfig {
+
+    String stringParam();
+
+    int intParam() default 5;
+
+    boolean boolParam();
+
+}
diff --git a/src/test/java/org/apache/sling/models/caconfig/example/caconfig/model/ConfigurationValuesModel.java b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/model/ConfigurationValuesModel.java
new file mode 100644
index 0000000..664463d
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/model/ConfigurationValuesModel.java
@@ -0,0 +1,58 @@
+/*
+ * 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.caconfig.example.caconfig.model;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.DefaultInjectionStrategy;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
+
+/**
+ * This model can be used to adapt configuration resource to a model.
+ */
+@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
+public class ConfigurationValuesModel {
+
+    @ValueMapValue
+    private String stringParam;
+    @ValueMapValue
+    private int intParam;
+    @ValueMapValue
+    private boolean boolParam;
+
+    public String getStringParam() {
+        return stringParam;
+    }
+    public void setStringParam(String stringParam) {
+        this.stringParam = stringParam;
+    }
+    public int getIntParam() {
+        return intParam;
+    }
+    public void setIntParam(int intParam) {
+        this.intParam = intParam;
+    }
+    public boolean isBoolParam() {
+        return boolParam;
+    }
+    public void setBoolParam(boolean boolParam) {
+        this.boolParam = boolParam;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationListModel.java
similarity index 56%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationListModel.java
index b5cef1e..71f2571 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationListModel.java
@@ -16,8 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.model;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import java.util.List;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
+public class InvalidAnnotationListModel {
+
+    // "Model" is not a valid caconfig annotation
+    @ContextAwareConfiguration
+    private List<Model> configList;
+
+    public List<Model> getConfigList() {
+        return configList;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationModel.java
similarity index 58%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationModel.java
index b5cef1e..f17ed25 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationModel.java
@@ -16,8 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.model;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
+public class InvalidAnnotationModel {
+
+    // "Model" is not a valid caconfig annotation
+    @ContextAwareConfiguration
+    private Model config;
+
+    public Model getConfig() {
+        return config;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidInjectModel.java
similarity index 54%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/model/InvalidInjectModel.java
index b5cef1e..3d98300 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidInjectModel.java
@@ -16,8 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.model;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import javax.inject.Inject;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.Source;
+import org.apache.sling.models.caconfig.example.caconfig.SingleConfig;
+
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
+public class InvalidInjectModel {
+
+    // only injection with explicit annotation @ContextAwareConfiguration is supported
+    @Inject
+    @Source("caconfig")
+    private SingleConfig config;
+
+    public SingleConfig getConfig() {
+        return config;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidSetModel.java
similarity index 54%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/model/InvalidSetModel.java
index b5cef1e..615cdc3 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidSetModel.java
@@ -16,8 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.model;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import java.util.Set;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+import org.apache.sling.models.caconfig.example.caconfig.ListConfig;
+
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
+public class InvalidSetModel {
+
+    // Set is not supported as collection type
+    @ContextAwareConfiguration
+    private Set<ListConfig> configList;
+
+    public Set<ListConfig> getConfigList() {
+        return configList;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigAdaptModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigAdaptModel.java
new file mode 100644
index 0000000..59838b2
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigAdaptModel.java
@@ -0,0 +1,57 @@
+/*
+ * 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.caconfig.example.model;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+import org.apache.sling.models.caconfig.example.caconfig.model.ConfigurationValuesModel;
+
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
+public class ListConfigAdaptModel implements ListConfigGetter<ConfigurationValuesModel> {
+
+    @ContextAwareConfiguration(name = "org.apache.sling.models.caconfig.example.caconfig.ListConfig")
+    private List<ConfigurationValuesModel> configList;
+
+    @ContextAwareConfiguration(name = "org.apache.sling.models.caconfig.example.caconfig.ListConfig")
+    private Collection<ConfigurationValuesModel> configCollection;
+
+    @ContextAwareConfiguration(name = "org.apache.sling.models.caconfig.example.caconfig.ListConfig")
+    private ConfigurationValuesModel[] configArray;
+
+    @Override
+    public List<ConfigurationValuesModel> getConfigList() {
+        return configList;
+    }
+
+    @Override
+    public Collection<ConfigurationValuesModel> getConfigCollection() {
+        return configCollection;
+    }
+
+    @Override
+    public ConfigurationValuesModel[] getConfigArray() {
+        return configArray;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigGetter.java
similarity index 76%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigGetter.java
index b5cef1e..a552c7a 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigGetter.java
@@ -16,8 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.model;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import java.util.Collection;
+import java.util.List;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+public interface ListConfigGetter<T> {
+
+    List<T> getConfigList();
+
+    Collection<T> getConfigCollection();
+
+    T[] getConfigArray();
+
+}
diff --git a/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigModel.java
new file mode 100644
index 0000000..ecc65df
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigModel.java
@@ -0,0 +1,57 @@
+/*
+ * 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.caconfig.example.model;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+import org.apache.sling.models.caconfig.example.caconfig.ListConfig;
+
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
+public class ListConfigModel implements ListConfigGetter<ListConfig> {
+
+    @ContextAwareConfiguration
+    private List<ListConfig> configList;
+
+    @ContextAwareConfiguration
+    private Collection<ListConfig> configCollection;
+
+    @ContextAwareConfiguration
+    private ListConfig[] configArray;
+
+    @Override
+    public List<ListConfig> getConfigList() {
+        return configList;
+    }
+
+    @Override
+    public Collection<ListConfig> getConfigCollection() {
+        return configCollection;
+    }
+
+    @Override
+    public ListConfig[] getConfigArray() {
+        return configArray;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigValueMapModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigValueMapModel.java
new file mode 100644
index 0000000..a83b6c4
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigValueMapModel.java
@@ -0,0 +1,57 @@
+/*
+ * 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.caconfig.example.model;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
+public class ListConfigValueMapModel implements ListConfigGetter<ValueMap> {
+
+    @ContextAwareConfiguration(name = "org.apache.sling.models.caconfig.example.caconfig.ListConfig")
+    private List<ValueMap> configList;
+
+    @ContextAwareConfiguration(name = "org.apache.sling.models.caconfig.example.caconfig.ListConfig")
+    private Collection<ValueMap> configCollection;
+
+    @ContextAwareConfiguration(name = "org.apache.sling.models.caconfig.example.caconfig.ListConfig")
+    private ValueMap[] configArray;
+
+    @Override
+    public List<ValueMap> getConfigList() {
+        return configList;
+    }
+
+    @Override
+    public Collection<ValueMap> getConfigCollection() {
+        return configCollection;
+    }
+
+    @Override
+    public ValueMap[] getConfigArray() {
+        return configArray;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigAdaptModel.java
similarity index 52%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigAdaptModel.java
index b5cef1e..ccfaf50 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigAdaptModel.java
@@ -16,8 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.model;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+import org.apache.sling.models.caconfig.example.caconfig.model.ConfigurationValuesModel;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
+public class SingleConfigAdaptModel implements SingleConfigGetter<ConfigurationValuesModel> {
+
+    @ContextAwareConfiguration(name = "testSingleConfig")
+    private ConfigurationValuesModel config;
+
+    @Override
+    public ConfigurationValuesModel getConfig() {
+        return config;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigGetter.java
similarity index 87%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigGetter.java
index b5cef1e..a69e327 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigGetter.java
@@ -16,8 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.model;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+public interface SingleConfigGetter<T> {
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+    T getConfig();
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigModel.java
similarity index 52%
copy from src/main/java/org/apache/sling/models/caconfig/package-info.java
copy to src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigModel.java
index b5cef1e..1243232 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigModel.java
@@ -16,8 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.model;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+import org.apache.sling.models.caconfig.example.caconfig.SingleConfig;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class, ResourceResolver.class })
+public class SingleConfigModel implements SingleConfigGetter<SingleConfig> {
+
+    @ContextAwareConfiguration
+    private SingleConfig config;
+
+    @Override
+    public SingleConfig getConfig() {
+        return config;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/caconfig/package-info.java b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigValueMapModel.java
similarity index 55%
rename from src/main/java/org/apache/sling/models/caconfig/package-info.java
rename to src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigValueMapModel.java
index b5cef1e..10a8939 100644
--- a/src/main/java/org/apache/sling/models/caconfig/package-info.java
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigValueMapModel.java
@@ -16,8 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.sling.models.caconfig.example.model;
 
-@Version("1.0.0")
-package org.apache.sling.models.caconfig;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
+public class SingleConfigValueMapModel implements SingleConfigGetter<ValueMap> {
+
+    @ContextAwareConfiguration(name = "testSingleConfig")
+    private ValueMap config;
+
+    @Override
+    public ValueMap getConfig() {
+        return config;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationInjectorTest.java b/src/test/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationInjectorTest.java
new file mode 100644
index 0000000..26f1e4b
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationInjectorTest.java
@@ -0,0 +1,243 @@
+/*
+ * 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.caconfig.impl.injectors;
+
+import static org.apache.sling.testing.mock.caconfig.ContextPlugins.CACONFIG;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.List;
+import java.util.function.Function;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.adapter.Adaptable;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.caconfig.management.multiplexer.ConfigurationInjectResourceDetectionStrategyMultiplexer;
+import org.apache.sling.caconfig.spi.ConfigurationInjectResourceDetectionStrategy;
+import org.apache.sling.models.caconfig.example.caconfig.ListConfig;
+import org.apache.sling.models.caconfig.example.caconfig.SingleConfig;
+import org.apache.sling.models.caconfig.example.caconfig.model.ConfigurationValuesModel;
+import org.apache.sling.models.caconfig.example.model.InvalidAnnotationListModel;
+import org.apache.sling.models.caconfig.example.model.InvalidAnnotationModel;
+import org.apache.sling.models.caconfig.example.model.InvalidInjectModel;
+import org.apache.sling.models.caconfig.example.model.InvalidSetModel;
+import org.apache.sling.models.caconfig.example.model.ListConfigAdaptModel;
+import org.apache.sling.models.caconfig.example.model.ListConfigGetter;
+import org.apache.sling.models.caconfig.example.model.ListConfigModel;
+import org.apache.sling.models.caconfig.example.model.ListConfigValueMapModel;
+import org.apache.sling.models.caconfig.example.model.SingleConfigAdaptModel;
+import org.apache.sling.models.caconfig.example.model.SingleConfigGetter;
+import org.apache.sling.models.caconfig.example.model.SingleConfigModel;
+import org.apache.sling.models.caconfig.example.model.SingleConfigValueMapModel;
+import org.apache.sling.testing.mock.caconfig.MockContextAwareConfig;
+import org.apache.sling.testing.mock.sling.junit5.SlingContext;
+import org.apache.sling.testing.mock.sling.junit5.SlingContextBuilder;
+import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.osgi.framework.Constants;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+@ExtendWith(SlingContextExtension.class)
+class ContextAwareConfigurationInjectorTest {
+
+    private final SlingContext context = new SlingContextBuilder()
+            .plugin(CACONFIG)
+            .build();
+
+    @BeforeEach
+    void setUp() {
+        MockContextAwareConfig.registerAnnotationPackages(context, "org.apache.sling.models.caconfig.example.caconfig");
+        context.addModelsForPackage("org.apache.sling.models.caconfig.example.model");
+
+        context.create().resource("/content/region/site",
+                "sling:configRef", "/conf/region/site");
+
+        context.currentResource(context.create().resource("/content/region/site/en"));
+
+        MockContextAwareConfig.writeConfiguration(context, "/content/region/site", SingleConfig.class,
+                "stringParam", "value1");
+
+        MockContextAwareConfig.writeConfigurationCollection(context, "/content/region/site", ListConfig.class, ImmutableList.of(
+                ImmutableMap.<String,Object>of("stringParam", "item1"),
+                ImmutableMap.<String,Object>of("stringParam", "item2")));
+    }
+
+    @Test
+    void testSingleConfigModel_Request() {
+        assertSingleConfig(SingleConfigModel.class, context.request(), SingleConfig::stringParam);
+    }
+
+    @Test
+    void testSingleConfigModel_Request_WithConfigurationInjectResourceDetectionStrategy() {
+        // set another resource as current resource which has not caconfig applied
+        Resource otherCurrentResource = context.currentResource(context.create().resource("/content/region2/site2/en2"));
+        // register a custom ConfigurationInjectResourceDetectionStrategy which redirects to a resource with caconfig available
+        context.registerService(ConfigurationInjectResourceDetectionStrategy.class, new ConfigurationInjectResourceDetectionStrategy() {
+            @Override
+            @SuppressWarnings("null")
+            public @Nullable Resource detectResource(@NotNull SlingHttpServletRequest request) {
+                if (StringUtils.equals(request.getResource().getPath(), otherCurrentResource.getPath())) {
+                    return context.resourceResolver().getResource("/content/region/site/en");
+                }
+                return null;
+            }
+        }, Constants.SERVICE_RANKING, 100);
+        assertSingleConfig(SingleConfigModel.class, context.request(), SingleConfig::stringParam);
+    }
+
+    @Test
+    void testSingleConfigModel_Request_WithConfigurationInjectResourceDetectionStrategy_NoStrategies() {
+        // simulate no registered strategies
+        context.registerService(ConfigurationInjectResourceDetectionStrategyMultiplexer.class, new ConfigurationInjectResourceDetectionStrategyMultiplexer() {
+            @Override
+            public @Nullable Resource detectResource(@NotNull SlingHttpServletRequest request) {
+                return null;
+            }
+
+        }, Constants.SERVICE_RANKING, 100);
+        assertSingleConfig(SingleConfigModel.class, context.request(), SingleConfig::stringParam);
+    }
+
+    @Test
+    void testSingleConfigModel_Resource() {
+        assertSingleConfig(SingleConfigModel.class, context.currentResource(), SingleConfig::stringParam);
+    }
+
+    @Test
+    void testSingleConfigValueMapModel_Request() {
+        assertSingleConfig(SingleConfigValueMapModel.class, context.request(), map -> map.get("stringParam", String.class));
+    }
+
+    @Test
+    void testSingleConfigValueMapModel_Resource() {
+        assertSingleConfig(SingleConfigValueMapModel.class, context.currentResource(), map -> map.get("stringParam", String.class));
+    }
+
+    @Test
+    void testSingleConfigAdaptModel_Request() {
+        assertSingleConfig(SingleConfigAdaptModel.class, context.request(), ConfigurationValuesModel::getStringParam);
+    }
+
+    @Test
+    void testSingleConfigAdaptModel_Resource() {
+        assertSingleConfig(SingleConfigAdaptModel.class, context.currentResource(), ConfigurationValuesModel::getStringParam);
+    }
+
+    @Test
+    void testListConfigModel_Request() {
+        assertListConfig(ListConfigModel.class, context.request(), ListConfig::stringParam);
+    }
+
+    @Test
+    void testListConfigModel_Resource() {
+        assertListConfig(ListConfigModel.class, context.currentResource(), ListConfig::stringParam);
+    }
+
+    @Test
+    void testListConfigValueMapModel_Request() {
+        assertListConfig(ListConfigValueMapModel.class, context.request(), map -> map.get("stringParam", String.class));
+    }
+
+    @Test
+    void testListConfigValueMapModel_Resource() {
+        assertListConfig(ListConfigValueMapModel.class, context.currentResource(), map -> map.get("stringParam", String.class));
+    }
+
+    @Test
+    void testListConfigAdaptModel_Request() {
+        assertListConfig(ListConfigAdaptModel.class, context.request(), ConfigurationValuesModel::getStringParam);
+    }
+
+    @Test
+    void testListConfigAdaptModel_Resource() {
+        assertListConfig(ListConfigAdaptModel.class, context.currentResource(), ConfigurationValuesModel::getStringParam);
+    }
+
+    private <T> void assertSingleConfig(Class<? extends SingleConfigGetter<T>> modelClass, Adaptable adaptable, Function<T,String> extractor) {
+        context.registerInjectActivateService(ContextAwareConfigurationInjector.class);
+        SingleConfigGetter<T> model = adaptable.adaptTo(modelClass);
+        assertNotNull(model);
+        T config = model.getConfig();
+        assertEquals("value1", extractor.apply(config));
+    }
+
+    private <T> void assertListConfig(Class<? extends ListConfigGetter<T>> modelClass, Adaptable adaptable, Function<T,String> extractor) {
+        context.registerInjectActivateService(ContextAwareConfigurationInjector.class);
+        ListConfigGetter<T> model = adaptable.adaptTo(modelClass);
+        assertNotNull(model);
+        assertListValues(model.getConfigList(), extractor);
+        assertListValues(ImmutableList.copyOf(model.getConfigCollection()), extractor);
+        assertListValues(ImmutableList.copyOf(model.getConfigArray()), extractor);
+    }
+
+    private <T> void assertListValues(List<T> configList, Function<T,String> extractor) {
+        assertNotNull(configList);
+        assertEquals(2, configList.size());
+        assertEquals("item1", extractor.apply(configList.get(0)));
+        assertEquals("item2", extractor.apply(configList.get(1)));
+    }
+
+    @Test
+    void testInvalid_SingleConfigModel_ResourceResolver() {
+        context.registerInjectActivateService(ContextAwareConfigurationInjector.class);
+        SingleConfigModel model = context.resourceResolver().adaptTo(SingleConfigModel.class);
+        assertNull(model);
+    }
+
+    @Test
+    @SuppressWarnings("null")
+    void testInvalidInjectModel() {
+        context.registerInjectActivateService(ContextAwareConfigurationInjector.class);
+        InvalidInjectModel model = context.request().adaptTo(InvalidInjectModel.class);
+        assertNull(model);
+    }
+
+    @Test
+    @SuppressWarnings("null")
+    void testInvalidAnnotationModel() {
+        context.registerInjectActivateService(ContextAwareConfigurationInjector.class);
+        InvalidAnnotationModel model = context.request().adaptTo(InvalidAnnotationModel.class);
+        assertNull(model);
+    }
+
+    @Test
+    @SuppressWarnings("null")
+    void testInvalidAnnotationListModel() {
+        context.registerInjectActivateService(ContextAwareConfigurationInjector.class);
+        InvalidAnnotationListModel model = context.request().adaptTo(InvalidAnnotationListModel.class);
+        assertNull(model);
+    }
+
+    @Test
+    @SuppressWarnings("null")
+    void testInvalidSetModel() {
+        context.registerInjectActivateService(ContextAwareConfigurationInjector.class);
+        InvalidSetModel model = context.request().adaptTo(InvalidSetModel.class);
+        assertNull(model);
+    }
+
+}