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 18:55:00 UTC

[sling-org-apache-sling-models-caconfig] branch feature/SLING-7256-caconfig-injector created (now d025ba9)

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

sseifert pushed a change to branch feature/SLING-7256-caconfig-injector
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-caconfig.git.


      at d025ba9  SLING-7256 implement injector

This branch includes the following new commits:

     new e71c05d  SLING-7256 define annotation
     new d025ba9  SLING-7256 implement injector

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[sling-org-apache-sling-models-caconfig] 01/02: SLING-7256 define annotation

Posted by ss...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sseifert pushed a commit to branch feature/SLING-7256-caconfig-injector
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-caconfig.git

commit e71c05dcec975a82cd2c14aa66c39b7eff7ddf33
Author: Stefan Seifert <st...@users.noreply.github.com>
AuthorDate: Wed Dec 22 15:51:47 2021 +0100

    SLING-7256 define annotation
---
 pom.xml                                            | 29 ++++++++-----
 .../annotations/ContextAwareConfiguration.java     | 48 ++++++++++++++++++++++
 .../caconfig/{ => annotations}/package-info.java   |  2 +-
 3 files changed, 67 insertions(+), 12 deletions(-)

diff --git a/pom.xml b/pom.xml
index a5d6f19..c790c39 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,30 +46,37 @@
     <dependencies>
 
         <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.models.api</artifactId>
-            <version>1.3.0</version>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.annotation.versioning</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.caconfig.api</artifactId>
-            <version>1.0.0</version>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.annotation.versioning</artifactId>
+            <artifactId>org.osgi.service.metatype.annotations</artifactId>
             <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.models.api</artifactId>
+            <version>1.3.8</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.service.metatype.annotations</artifactId>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.caconfig.api</artifactId>
+            <version>1.1.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.caconfig.spi</artifactId>
+            <version>1.3.4</version>
             <scope>provided</scope>
         </dependency>
 
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..6c330c0
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/caconfig/annotations/ContextAwareConfiguration.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+@Target({ METHOD, FIELD, PARAMETER })
+@Retention(RUNTIME)
+@InjectAnnotation
+@Source("caconfig")
+public @interface ContextAwareConfiguration {
+
+    /**
+     * 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%
rename from src/main/java/org/apache/sling/models/caconfig/package-info.java
rename 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

[sling-org-apache-sling-models-caconfig] 02/02: SLING-7256 implement injector

Posted by ss...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sseifert pushed a commit to branch feature/SLING-7256-caconfig-injector
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-caconfig.git

commit d025ba9b18c205e1dab4bbd55672fe051d76b761
Author: Stefan Seifert <st...@users.noreply.github.com>
AuthorDate: Wed Dec 22 17:07:43 2021 +0100

    SLING-7256 implement injector
---
 pom.xml                                            |  34 ++--
 .../annotations/ContextAwareConfiguration.java     |   9 +
 .../ContextAwareConfigurationInjector.java         | 189 +++++++++++++++++++
 .../ContextAwareConfigurationProcessor.java        |  38 ++++
 .../caconfig/example/caconfig/ListConfig.java      |  32 ++++
 .../caconfig/example/caconfig/SingleConfig.java    |  32 ++++
 .../caconfig/model/ConfigurationValuesModel.java   |  58 ++++++
 .../example/model/InvalidAnnotationListModel.java  |  39 ++++
 .../example/model/InvalidAnnotationModel.java      |  37 ++++
 .../caconfig/example/model/InvalidInjectModel.java |  41 +++++
 .../caconfig/example/model/InvalidSetModel.java    |  40 +++++
 .../example/model/ListConfigAdaptModel.java        |  57 ++++++
 .../caconfig/example/model/ListConfigGetter.java   |  32 ++++
 .../caconfig/example/model/ListConfigModel.java    |  57 ++++++
 .../example/model/ListConfigValueMapModel.java     |  57 ++++++
 .../example/model/SingleConfigAdaptModel.java      |  38 ++++
 .../caconfig/example/model/SingleConfigGetter.java |  25 +++
 .../caconfig/example/model/SingleConfigModel.java  |  39 ++++
 .../example/model/SingleConfigValueMapModel.java   |  38 ++++
 .../ContextAwareConfigurationInjectorTest.java     | 199 +++++++++++++++++++++
 20 files changed, 1078 insertions(+), 13 deletions(-)

diff --git a/pom.xml b/pom.xml
index c790c39..80a2f50 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,12 @@
 
         <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.8</version>
             <scope>provided</scope>
@@ -79,16 +85,16 @@
             <version>1.3.4</version>
             <scope>provided</scope>
         </dependency>
-
         <dependency>
-            <groupId>org.jetbrains</groupId>
-            <artifactId>annotations</artifactId>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.4</version>
             <scope>provided</scope>
         </dependency>
+
         <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.api</artifactId>
-            <version>2.4.0</version>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -110,7 +116,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>
@@ -121,14 +127,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.3.6</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
index 6c330c0..0c527b5 100644
--- a/src/main/java/org/apache/sling/models/caconfig/annotations/ContextAwareConfiguration.java
+++ b/src/main/java/org/apache/sling/models/caconfig/annotations/ContextAwareConfiguration.java
@@ -30,6 +30,8 @@ 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)
@@ -38,6 +40,13 @@ import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
 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.
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..a475a5f
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationInjector.java
@@ -0,0 +1,189 @@
+/*
+ * 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.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;
+
+    @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;
+            return request.getResource();
+        }
+        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/impl/injectors/ContextAwareConfigurationProcessor.java b/src/main/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationProcessor.java
new file mode 100644
index 0000000..30bcec4
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationProcessor.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
+import org.apache.sling.models.caconfig.annotations.ContextAwareConfiguration;
+import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor2;
+
+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/test/java/org/apache/sling/models/caconfig/example/caconfig/ListConfig.java b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/ListConfig.java
new file mode 100644
index 0000000..104419d
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/ListConfig.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+import org.apache.sling.caconfig.annotation.Configuration;
+
+@Configuration(collection = true)
+public @interface ListConfig {
+
+    String stringParam();
+
+    int intParam() default 5;
+
+    boolean boolParam();
+
+}
diff --git a/src/test/java/org/apache/sling/models/caconfig/example/caconfig/SingleConfig.java b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/SingleConfig.java
new file mode 100644
index 0000000..61ee707
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/caconfig/SingleConfig.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+import org.apache.sling.caconfig.annotation.Configuration;
+
+@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/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationListModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationListModel.java
new file mode 100644
index 0000000..71f2571
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationListModel.java
@@ -0,0 +1,39 @@
+/*
+ * 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.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;
+
+@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/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationModel.java
new file mode 100644
index 0000000..f17ed25
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidAnnotationModel.java
@@ -0,0 +1,37 @@
+/*
+ * 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 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 InvalidAnnotationModel {
+
+    // "Model" is not a valid caconfig annotation
+    @ContextAwareConfiguration
+    private Model config;
+
+    public Model getConfig() {
+        return config;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidInjectModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidInjectModel.java
new file mode 100644
index 0000000..3d98300
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidInjectModel.java
@@ -0,0 +1,41 @@
+/*
+ * 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 javax.inject.Inject;
+
+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/test/java/org/apache/sling/models/caconfig/example/model/InvalidSetModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidSetModel.java
new file mode 100644
index 0000000..615cdc3
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/InvalidSetModel.java
@@ -0,0 +1,40 @@
+/*
+ * 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.Set;
+
+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/test/java/org/apache/sling/models/caconfig/example/model/ListConfigGetter.java b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigGetter.java
new file mode 100644
index 0000000..a552c7a
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/ListConfigGetter.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+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/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigAdaptModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigAdaptModel.java
new file mode 100644
index 0000000..ccfaf50
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigAdaptModel.java
@@ -0,0 +1,38 @@
+/*
+ * 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 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 SingleConfigAdaptModel implements SingleConfigGetter<ConfigurationValuesModel> {
+
+    @ContextAwareConfiguration(name = "testSingleConfig")
+    private ConfigurationValuesModel config;
+
+    @Override
+    public ConfigurationValuesModel getConfig() {
+        return config;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigGetter.java b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigGetter.java
new file mode 100644
index 0000000..a69e327
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigGetter.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+public interface SingleConfigGetter<T> {
+
+    T getConfig();
+
+}
diff --git a/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigModel.java
new file mode 100644
index 0000000..1243232
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigModel.java
@@ -0,0 +1,39 @@
+/*
+ * 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 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;
+
+@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/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigValueMapModel.java b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigValueMapModel.java
new file mode 100644
index 0000000..10a8939
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/example/model/SingleConfigValueMapModel.java
@@ -0,0 +1,38 @@
+/*
+ * 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 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 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..977ae5e
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/caconfig/impl/injectors/ContextAwareConfigurationInjectorTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.sling.api.adapter.Adaptable;
+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.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+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")));
+
+        context.registerInjectActivateService(ContextAwareConfigurationInjector.class);
+    }
+
+    @Test
+    void testSingleConfigModel_Request() {
+        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) {
+        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) {
+        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() {
+        SingleConfigModel model = context.resourceResolver().adaptTo(SingleConfigModel.class);
+        assertNull(model);
+    }
+
+    @Test
+    @SuppressWarnings("null")
+    void testInvalidInjectModel() {
+        InvalidInjectModel model = context.request().adaptTo(InvalidInjectModel.class);
+        assertNull(model);
+    }
+
+    @Test
+    @SuppressWarnings("null")
+    void testInvalidAnnotationModel() {
+        InvalidAnnotationModel model = context.request().adaptTo(InvalidAnnotationModel.class);
+        assertNull(model);
+    }
+
+    @Test
+    @SuppressWarnings("null")
+    void testInvalidAnnotationListModel() {
+        InvalidAnnotationListModel model = context.request().adaptTo(InvalidAnnotationListModel.class);
+        assertNull(model);
+    }
+
+    @Test
+    @SuppressWarnings("null")
+    void testInvalidSetModel() {
+        InvalidSetModel model = context.request().adaptTo(InvalidSetModel.class);
+        assertNull(model);
+    }
+
+}