SLING-11839 Support writing nested configurations and configuration collections
diff --git a/src/main/java/org/apache/sling/testing/mock/caconfig/ConfigurationDataParts.java b/src/main/java/org/apache/sling/testing/mock/caconfig/ConfigurationDataParts.java
new file mode 100644
index 0000000..080492d
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/caconfig/ConfigurationDataParts.java
@@ -0,0 +1,63 @@
+/*
+ * 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.testing.mock.caconfig;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Splits a list of key/value pairs which may contain nested configuration and nested configuration lists in it's parts.
+ */
+class ConfigurationDataParts {
+
+ private final Map<String,Object> values = new TreeMap<>();
+ private final Map<String,Map<String,Object>> maps = new TreeMap<>();
+ private final Map<String,Collection<Map<String,Object>>> collections = new TreeMap<>();
+
+ @SuppressWarnings("unchecked")
+ ConfigurationDataParts(Map<String,Object> input) {
+ for (Map.Entry<String, Object> entry : input.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (value instanceof Map) {
+ maps.put(key, (Map)value);
+ }
+ else if (value instanceof Collection) {
+ collections.put(key, (Collection)value);
+ }
+ else {
+ values.put(key, value);
+ }
+ }
+ }
+
+ Map<String, Object> getValues() {
+ return values;
+ }
+
+ Map<String, Map<String, Object>> getMaps() {
+ return maps;
+ }
+
+ Map<String, Collection<Map<String, Object>>> getCollections() {
+ return collections;
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/caconfig/ConfigurationPersistHelper.java b/src/main/java/org/apache/sling/testing/mock/caconfig/ConfigurationPersistHelper.java
new file mode 100644
index 0000000..db39882
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/caconfig/ConfigurationPersistHelper.java
@@ -0,0 +1,128 @@
+/*
+ * 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.testing.mock.caconfig;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.caconfig.management.ConfigurationManager;
+import org.apache.sling.caconfig.management.multiplexer.ConfigurationPersistenceStrategyMultiplexer;
+import org.apache.sling.caconfig.spi.ConfigurationCollectionPersistData;
+import org.apache.sling.caconfig.spi.ConfigurationPersistData;
+import org.apache.sling.testing.mock.sling.context.SlingContextImpl;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Writes context-aware configuration contains in maps and nested maps via {@link ConfigurationManager} to repository.
+ */
+class ConfigurationPersistHelper {
+
+ private final ConfigurationManager configManager;
+ private final ConfigurationPersistenceStrategyMultiplexer configurationPersistenceStrategy;
+ private final Resource contextResource;
+
+ /**
+ * @param context Sling context
+ * @param contextPath Context path
+ */
+ ConfigurationPersistHelper(@NotNull SlingContextImpl context, @NotNull String contextPath) {
+ configManager = context.getService(ConfigurationManager.class);
+ configurationPersistenceStrategy = context.getService(ConfigurationPersistenceStrategyMultiplexer.class);
+ contextResource = context.resourceResolver().getResource(contextPath);
+ if (contextResource == null) {
+ throw new IllegalArgumentException("No resource found at" + contextPath);
+ }
+ }
+
+ /**
+ * Writes configuration parameters using the primary configured persistence
+ * provider.
+ * @param configName Config name
+ * @param values Configuration values
+ */
+ void writeConfiguration(@NotNull String configName, @NotNull Map<String, Object> values) {
+ // write properties of main configuration
+ ConfigurationDataParts parts = new ConfigurationDataParts(values);
+ configManager.persistConfiguration(contextResource, configName, new ConfigurationPersistData(parts.getValues()));
+
+ // write nested configuration and nested configuration collections
+ for (Map.Entry<String,Map<String,Object>> nestedMap : parts.getMaps().entrySet()) {
+ writeConfiguration(getNestedConfigName(configName, nestedMap.getKey()), nestedMap.getValue());
+ }
+ for (Map.Entry<String,Collection<Map<String,Object>>> nestedCollection : parts.getCollections().entrySet()) {
+ writeConfigurationCollection(getNestedConfigName(configName, nestedCollection.getKey()), nestedCollection.getValue());
+ }
+ }
+
+ /**
+ * Writes a collection of configuration parameters using the primary configured persistence provider.
+ * @param configName Config name
+ * @param values Configuration values
+ */
+ void writeConfigurationCollection(@NotNull String configName, @NotNull Collection<@NotNull Map<String, Object>> values) {
+ // split each collection item map in it's parts
+ Map<String, ConfigurationDataParts> partsCollection = new HashMap<>();
+ int index = 0;
+ for (Map<String, Object> map : values) {
+ partsCollection.put("item" + (index++), new ConfigurationDataParts(map));
+ }
+
+ // write properties of main configuration collection
+ List<ConfigurationPersistData> items = partsCollection.entrySet().stream()
+ .map(entry -> new ConfigurationPersistData(entry.getValue().getValues()).collectionItemName(entry.getKey()))
+ .collect(Collectors.toList());
+ configManager.persistConfigurationCollection(contextResource, configName,
+ new ConfigurationCollectionPersistData(items));
+
+ // write nested configuration and nested configuration collections
+ for (Map.Entry<String, ConfigurationDataParts> entry : partsCollection.entrySet()) {
+ String itemName = entry.getKey();
+ ConfigurationDataParts parts = entry.getValue();
+ for (Map.Entry<String,Map<String,Object>> nestedMap : parts.getMaps().entrySet()) {
+ writeConfiguration(getNestedCollectionItemConfigName(configName, itemName, nestedMap.getKey()), nestedMap.getValue());
+ }
+ for (Map.Entry<String,Collection<Map<String,Object>>> nestedCollection : parts.getCollections().entrySet()) {
+ writeConfigurationCollection(getNestedCollectionItemConfigName(configName, itemName, nestedCollection.getKey()), nestedCollection.getValue());
+ }
+ }
+ }
+
+ private String getNestedConfigName(@NotNull String configName, @NotNull String key) {
+ String nestedKey = configName + "/" + key;
+ String nestedConfigName = configurationPersistenceStrategy.getConfigName(nestedKey, null);
+ if (nestedConfigName == null) {
+ throw new IllegalArgumentException("Nested configuration not supported for " + nestedKey);
+ }
+ return nestedConfigName;
+ }
+
+ private String getNestedCollectionItemConfigName(@NotNull String configName, @NotNull String itemName, @NotNull String key) {
+ String nestedKey = configName + "/" + itemName + "/" + key;
+ String nestedConfigName = configurationPersistenceStrategy.getCollectionItemConfigName(nestedKey, null);
+ if (nestedConfigName == null) {
+ throw new IllegalArgumentException("Nested configuration not supported for " + nestedKey);
+ }
+ return nestedConfigName;
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/caconfig/MockContextAwareConfig.java b/src/main/java/org/apache/sling/testing/mock/caconfig/MockContextAwareConfig.java
index 7bde85b..7ffaf71 100644
--- a/src/main/java/org/apache/sling/testing/mock/caconfig/MockContextAwareConfig.java
+++ b/src/main/java/org/apache/sling/testing/mock/caconfig/MockContextAwareConfig.java
@@ -18,17 +18,11 @@
*/
package org.apache.sling.testing.mock.caconfig;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
-import org.apache.sling.api.resource.Resource;
import org.apache.sling.caconfig.annotation.Configuration;
-import org.apache.sling.caconfig.management.ConfigurationManager;
-import org.apache.sling.caconfig.spi.ConfigurationCollectionPersistData;
-import org.apache.sling.caconfig.spi.ConfigurationPersistData;
import org.apache.sling.testing.mock.osgi.MapUtil;
import org.apache.sling.testing.mock.sling.context.SlingContextImpl;
import org.jetbrains.annotations.NotNull;
@@ -76,7 +70,7 @@
* Writes configuration parameters using the primary configured persistence
* provider.
* @param context Sling context
- * @param contextPath Configuration id
+ * @param contextPath Context path
* @param configClass Configuration class
* @param values Configuration values
*/
@@ -89,23 +83,21 @@
* Writes configuration parameters using the primary configured persistence
* provider.
* @param context Sling context
- * @param contextPath Configuration id
+ * @param contextPath Context path
* @param configName Config name
* @param values Configuration values
*/
- @SuppressWarnings("null")
public static void writeConfiguration(@NotNull SlingContextImpl context, @NotNull String contextPath, @NotNull String configName,
@NotNull Map<String, Object> values) {
- ConfigurationManager configManager = context.getService(ConfigurationManager.class);
- Resource contextResource = context.resourceResolver().getResource(contextPath);
- configManager.persistConfiguration(contextResource, configName, new ConfigurationPersistData(values));
+ ConfigurationPersistHelper helper = new ConfigurationPersistHelper(context, contextPath);
+ helper.writeConfiguration(configName, values);
}
/**
* Writes configuration parameters using the primary configured persistence
* provider.
* @param context Sling context
- * @param contextPath Configuration id
+ * @param contextPath Context path
* @param configClass Configuration class
* @param values Configuration values
*/
@@ -117,7 +109,7 @@
* Writes configuration parameters using the primary configured persistence
* provider.
* @param context Sling context
- * @param contextPath Configuration id
+ * @param contextPath Context path
* @param configName Config name
* @param values Configuration values
*/
@@ -129,7 +121,7 @@
* Writes a collection of configuration parameters using the primary
* configured persistence provider.
* @param context Sling context
- * @param contextPath Configuration id
+ * @param contextPath Context path
* @param configClass Configuration class
* @param values Configuration values
*/
@@ -142,25 +134,14 @@
* Writes a collection of configuration parameters using the primary
* configured persistence provider.
* @param context Sling context
- * @param contextPath Configuration id
+ * @param contextPath Context path
* @param configName Config name
* @param values Configuration values
*/
- @SuppressWarnings("null")
public static void writeConfigurationCollection(@NotNull SlingContextImpl context, @NotNull String contextPath, @NotNull String configName,
@NotNull Collection<@NotNull Map<String, Object>> values) {
- ConfigurationManager configManager = context.getService(ConfigurationManager.class);
- Resource contextResource = context.resourceResolver().getResource(contextPath);
- if (contextResource == null) {
- throw new IllegalArgumentException("No resource found at" + contextPath);
- }
- List<ConfigurationPersistData> items = new ArrayList<>();
- int index = 0;
- for (Map<String, Object> map : values) {
- items.add(new ConfigurationPersistData(map).collectionItemName("item" + (index++)));
- }
- configManager.persistConfigurationCollection(contextResource, configName,
- new ConfigurationCollectionPersistData(items));
+ ConfigurationPersistHelper helper = new ConfigurationPersistHelper(context, contextPath);
+ helper.writeConfigurationCollection(configName, values);
}
@SuppressWarnings("null")
diff --git a/src/test/java/org/apache/sling/testing/mock/caconfig/MockContextAwareConfigTest.java b/src/test/java/org/apache/sling/testing/mock/caconfig/MockContextAwareConfigTest.java
index 412c04a..0e448e5 100644
--- a/src/test/java/org/apache/sling/testing/mock/caconfig/MockContextAwareConfigTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/caconfig/MockContextAwareConfigTest.java
@@ -19,6 +19,7 @@
package org.apache.sling.testing.mock.caconfig;
import static org.apache.sling.testing.mock.caconfig.ContextPlugins.CACONFIG;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -28,9 +29,16 @@
import org.apache.sling.api.resource.Resource;
import org.apache.sling.caconfig.ConfigurationBuilder;
import org.apache.sling.testing.mock.caconfig.example.ListConfig;
+import org.apache.sling.testing.mock.caconfig.example.NestedConfig;
+import org.apache.sling.testing.mock.caconfig.example.NestedConfigSub;
+import org.apache.sling.testing.mock.caconfig.example.NestedConfigSub2;
+import org.apache.sling.testing.mock.caconfig.example.NestedListConfig;
import org.apache.sling.testing.mock.caconfig.example.SimpleConfig;
+import org.apache.sling.testing.mock.sling.builder.ImmutableValueMap;
import org.apache.sling.testing.mock.sling.junit.SlingContext;
import org.apache.sling.testing.mock.sling.junit.SlingContextBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -38,7 +46,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-@SuppressWarnings("null")
public class MockContextAwareConfigTest {
@Rule
@@ -53,28 +60,25 @@
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", SimpleConfig.class,
- "stringParam", "value1");
-
- MockContextAwareConfig.writeConfigurationCollection(context, "/content/region/site", ListConfig.class, ImmutableList.of(
- ImmutableMap.<String,Object>of("stringParam", "value1"),
- ImmutableMap.<String,Object>of("stringParam", "value2")));
}
@Test
public void testSingletonConfig() {
- Resource resource = context.request().getResource();
- SimpleConfig config = resource.adaptTo(ConfigurationBuilder.class).as(SimpleConfig.class);
- assertNotNull(config);
+ MockContextAwareConfig.writeConfiguration(context, "/content/region/site", SimpleConfig.class,
+ "stringParam", "value1");
+
+ SimpleConfig config = getConfig(SimpleConfig.class);
assertEquals("value1", config.stringParam());
assertEquals(5, config.intParam());
}
@Test
- public void testConfigCollection() {
- Resource resource = context.request().getResource();
- Collection<ListConfig> config = resource.adaptTo(ConfigurationBuilder.class).asCollection(ListConfig.class);
+ public void testCollectionConfig() {
+ MockContextAwareConfig.writeConfigurationCollection(context, "/content/region/site", ListConfig.class, ImmutableList.of(
+ ImmutableMap.<String,Object>of("stringParam", "value1"),
+ ImmutableMap.<String,Object>of("stringParam", "value2")));
+
+ Collection<ListConfig> config = getConfigCollection(ListConfig.class);
assertEquals(2, config.size());
Iterator<ListConfig> items = config.iterator();
@@ -87,4 +91,102 @@
assertEquals(5, item2.intParam());
}
+ @Test
+ public void testNestedSingletonConfig() {
+ MockContextAwareConfig.writeConfiguration(context, "/content/region/site", NestedConfig.class,
+ "stringParam", "value1",
+ "sub", ImmutableList.of(
+ ImmutableValueMap.of("subStringParam", "v1", "intParam", 5, "stringArrayParam", new String[] {"v1a","v1b"}),
+ ImmutableValueMap.of("subStringParam", "v2")),
+ "sub2", ImmutableValueMap.of(
+ "sub2StringParam", "v3",
+ "sub", ImmutableValueMap.of("subStringParam", "v4"),
+ "subList", ImmutableList.of(ImmutableValueMap.of("subStringParam", "v5a"),ImmutableValueMap.of("subStringParam", "v5b"))),
+ "sub2List", ImmutableList.of(
+ ImmutableValueMap.of("sub2StringParam", "v6")));
+
+ NestedConfig config = getConfig(NestedConfig.class);
+ assertEquals("value1", config.stringParam());
+
+ NestedConfigSub[] sub = config.sub();
+ assertEquals(2, sub.length);
+ assertEquals("v1", sub[0].subStringParam());
+ assertEquals(5, sub[0].intParam());
+ assertArrayEquals(new String[] {"v1a","v1b"}, sub[0].stringArrayParam());
+ assertEquals("v2", sub[1].subStringParam());
+
+ NestedConfigSub2 sub2 = config.sub2();
+ assertEquals("v3", sub2.sub2StringParam());
+ assertEquals("v4", sub2.sub().subStringParam());
+ NestedConfigSub[] sub2_sublist = sub2.subList();
+ assertEquals(2, sub2_sublist.length);
+ assertEquals("v5a", sub2_sublist[0].subStringParam());
+ assertEquals("v5b", sub2_sublist[1].subStringParam());
+
+ NestedConfigSub2[] sub2list = config.sub2List();
+ assertEquals(1, sub2list.length);
+ assertEquals("v6", sub2list[0].sub2StringParam());
+ }
+
+ @Test
+ public void testNestedCollectionConfigConfig() {
+ MockContextAwareConfig.writeConfigurationCollection(context, "/content/region/site", NestedListConfig.class, ImmutableList.of(
+ ImmutableMap.<String,Object>of("stringParam", "value1",
+ "sub", ImmutableList.of(
+ ImmutableValueMap.of("subStringParam", "v1", "intParam", 5, "stringArrayParam", new String[] {"v1a","v1b"}),
+ ImmutableValueMap.of("subStringParam", "v2")),
+ "sub2", ImmutableValueMap.of(
+ "sub2StringParam", "v3",
+ "sub", ImmutableValueMap.of("subStringParam", "v4"),
+ "subList", ImmutableList.of(ImmutableValueMap.of("subStringParam", "v5a"),ImmutableValueMap.of("subStringParam", "v5b"))),
+ "sub2List", ImmutableList.of(
+ ImmutableValueMap.of("sub2StringParam", "v6"))),
+ ImmutableMap.<String,Object>of("stringParam", "value2")));
+
+ Collection<NestedListConfig> config = getConfigCollection(NestedListConfig.class);
+ assertEquals(2, config.size());
+ Iterator<NestedListConfig> items = config.iterator();
+
+ NestedListConfig item1 = items.next();
+ assertEquals("value1", item1.stringParam());
+
+ NestedConfigSub[] sub = item1.sub();
+ assertEquals(2, sub.length);
+ assertEquals("v1", sub[0].subStringParam());
+ assertEquals(5, sub[0].intParam());
+ assertArrayEquals(new String[] {"v1a","v1b"}, sub[0].stringArrayParam());
+ assertEquals("v2", sub[1].subStringParam());
+
+ NestedConfigSub2 sub2 = item1.sub2();
+ assertEquals("v3", sub2.sub2StringParam());
+ assertEquals("v4", sub2.sub().subStringParam());
+ NestedConfigSub[] sub2_sublist = sub2.subList();
+ assertEquals(2, sub2_sublist.length);
+ assertEquals("v5a", sub2_sublist[0].subStringParam());
+ assertEquals("v5b", sub2_sublist[1].subStringParam());
+
+ NestedConfigSub2[] sub2list = item1.sub2List();
+ assertEquals(1, sub2list.length);
+ assertEquals("v6", sub2list[0].sub2StringParam());
+
+ NestedListConfig item2 = items.next();
+ assertEquals("value2", item2.stringParam());
+ }
+
+ @SuppressWarnings("null")
+ private <T> @NotNull T getConfig(@NotNull Class<T> configClass) {
+ Resource resource = context.request().getResource();
+ T result = resource.adaptTo(ConfigurationBuilder.class).as(configClass);
+ assertNotNull(result);
+ return result;
+ }
+
+ @SuppressWarnings("null")
+ private <T> @NotNull Collection<@Nullable T> getConfigCollection(@NotNull Class<T> configClass) {
+ Resource resource = context.request().getResource();
+ Collection<@Nullable T> result = resource.adaptTo(ConfigurationBuilder.class).asCollection(configClass);
+ assertNotNull(result);
+ return result;
+ }
+
}
diff --git a/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedConfig.java b/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedConfig.java
new file mode 100644
index 0000000..80fd4aa
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedConfig.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.testing.mock.caconfig.example;
+
+import org.apache.sling.caconfig.annotation.Configuration;
+
+/**
+ * Nested configuration example.
+ */
+@Configuration
+public @interface NestedConfig {
+
+ String stringParam();
+
+ NestedConfigSub[] sub();
+
+ NestedConfigSub2 sub2();
+
+ NestedConfigSub2[] sub2List();
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedConfigSub.java b/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedConfigSub.java
new file mode 100644
index 0000000..8714c9e
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedConfigSub.java
@@ -0,0 +1,34 @@
+/*
+ * 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.testing.mock.caconfig.example;
+
+/**
+ * Nested sub config.
+ */
+public @interface NestedConfigSub {
+
+ String subStringParam();
+
+ int intParam();
+
+ String[] stringArrayParam() default {
+ "value1", "value2"
+ };
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedConfigSub2.java b/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedConfigSub2.java
new file mode 100644
index 0000000..c640056
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedConfigSub2.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.testing.mock.caconfig.example;
+
+/**
+ * Nested sub config 2.
+ */
+public @interface NestedConfigSub2 {
+
+ String sub2StringParam();
+
+ NestedConfigSub sub();
+
+ NestedConfigSub[] subList();
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedListConfig.java b/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedListConfig.java
new file mode 100644
index 0000000..28e7c27
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/caconfig/example/NestedListConfig.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.testing.mock.caconfig.example;
+
+import org.apache.sling.caconfig.annotation.Configuration;
+
+/**
+ * Nested configuration list example.
+ */
+@Configuration(collection = true)
+public @interface NestedListConfig {
+
+ String stringParam();
+
+ NestedConfigSub[] sub();
+
+ NestedConfigSub2 sub2();
+
+ NestedConfigSub2[] sub2List();
+
+}