Author: sseifert
Date: Fri Sep 29 17:01:44 2017
New Revision: 1810138

URL: http://svn.apache.org/viewvc?rev=1810138&view=rev
Log:
SLING-7165 CAConfig Impl: Potential data loss when saving multiple nested 
configuration lists

Modified:
    
sling/trunk/bundles/extensions/caconfig/impl/src/main/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategy.java
    
sling/trunk/bundles/extensions/caconfig/impl/src/test/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategyTest.java

Modified: 
sling/trunk/bundles/extensions/caconfig/impl/src/main/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategy.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/caconfig/impl/src/main/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategy.java?rev=1810138&r1=1810137&r2=1810138&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/caconfig/impl/src/main/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategy.java
 (original)
+++ 
sling/trunk/bundles/extensions/caconfig/impl/src/main/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategy.java
 Fri Sep 29 17:01:44 2017
@@ -169,7 +169,7 @@ public class DefaultConfigurationPersist
         Resource configResourceParent = getOrCreateResource(resourceResolver, 
configResourceCollectionParentPath, data.getProperties()); 
         
         // delete existing children and create new ones
-        deleteChildren(configResourceParent);
+        deleteChildrenNotInCollection(configResourceParent, data);
         for (ConfigurationPersistData item : data.getItems()) {
             String path = configResourceParent.getPath() + "/" + 
item.getCollectionItemName();
             getOrCreateResource(resourceResolver, path, item.getProperties());
@@ -211,12 +211,25 @@ public class DefaultConfigurationPersist
         }
     }
 
-    private void deleteChildren(Resource resource) {
+    /**
+     * Delete children that are no longer contained in list of collection 
items. 
+     * @param resource Parent resource
+     * @param data List of collection items
+     */
+    private void deleteChildrenNotInCollection(Resource resource, 
ConfigurationCollectionPersistData data) {
         ResourceResolver resourceResolver = resource.getResourceResolver();
+
+        Set<String> collectionItemNames = new HashSet<>();
+        for (ConfigurationPersistData item : data.getItems()) {
+            collectionItemNames.add(item.getCollectionItemName());
+        }
+        
         try {
             for (Resource child : resource.getChildren()) {
-                log.trace("! Delete resource {}", child.getPath());
-                resourceResolver.delete(child);
+                if (!collectionItemNames.contains(child.getName())) {
+                    log.trace("! Delete resource {}", child.getPath());
+                    resourceResolver.delete(child);
+                }
             }
         }
         catch (PersistenceException ex) {

Modified: 
sling/trunk/bundles/extensions/caconfig/impl/src/test/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategyTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/caconfig/impl/src/test/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategyTest.java?rev=1810138&r1=1810137&r2=1810138&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/caconfig/impl/src/test/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategyTest.java
 (original)
+++ 
sling/trunk/bundles/extensions/caconfig/impl/src/test/java/org/apache/sling/caconfig/impl/def/DefaultConfigurationPersistenceStrategyTest.java
 Fri Sep 29 17:01:44 2017
@@ -32,6 +32,7 @@ import org.apache.sling.caconfig.spi.Con
 import org.apache.sling.caconfig.spi.ConfigurationPersistData;
 import org.apache.sling.caconfig.spi.ConfigurationPersistenceStrategy2;
 import org.apache.sling.hamcrest.ResourceMatchers;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
 import org.apache.sling.testing.mock.sling.junit.SlingContext;
 import org.junit.Before;
 import org.junit.Rule;
@@ -43,7 +44,7 @@ import com.google.common.collect.Immutab
 public class DefaultConfigurationPersistenceStrategyTest {
 
     @Rule
-    public SlingContext context = new SlingContext();
+    public SlingContext context = new 
SlingContext(ResourceResolverType.JCR_MOCK);
     
     @Before
     public void setUp() {
@@ -113,22 +114,97 @@ public class DefaultConfigurationPersist
         // store new config collection items
         
assertTrue(underTest.persistConfigurationCollection(context.resourceResolver(), 
"/conf/test",
                 new ConfigurationCollectionPersistData(ImmutableList.of(
-                new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value1")).collectionItemName("0"),
-                new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop2", 
5)).collectionItemName("1"))
+                new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value1")).collectionItemName("item1"),
+                new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop2", 
5)).collectionItemName("item2"))
                 ).properties(ImmutableMap.<String, Object>of("prop1", "abc", 
"sling:resourceType", "/a/b/c"))
         ));
         context.resourceResolver().commit();
         
         Resource resource = 
context.resourceResolver().getResource("/conf/test");
-        assertEquals(2, ImmutableList.copyOf(resource.getChildren()).size());
-        ValueMap props0 = 
context.resourceResolver().getResource("/conf/test/0").getValueMap();
-        assertEquals("value1", props0.get("prop1", String.class));
-        ValueMap props1 = 
context.resourceResolver().getResource("/conf/test/1").getValueMap();
-        assertEquals((Integer)5, props1.get("prop2", Integer.class));
-        
-        assertThat(resource, ResourceMatchers.props(
-                "prop1", "abc",
-                "sling:resourceType", "/a/b/c"));
+        assertThat(resource, ResourceMatchers.props("prop1", "abc", 
"sling:resourceType", "/a/b/c"));
+        assertThat(resource, ResourceMatchers.containsChildren("item1", 
"item2"));
+
+        assertThat(resource.getChild("item1"), ResourceMatchers.props("prop1", 
"value1"));
+        assertThat(resource.getChild("item2"), ResourceMatchers.props("prop2", 
5L));        
+
+        // remove config collection items
+        
assertTrue(underTest.persistConfigurationCollection(context.resourceResolver(), 
"/conf/test",
+                new 
ConfigurationCollectionPersistData(ImmutableList.<ConfigurationPersistData>of())));
+        context.resourceResolver().commit();
+
+        resource = context.resourceResolver().getResource("/conf/test");
+        assertEquals(0, ImmutableList.copyOf(resource.getChildren()).size());
+
+        underTest.deleteConfiguration(context.resourceResolver(), 
"/conf/test");
+        assertNull(context.resourceResolver().getResource("/conf/test"));
+    }
+
+    @Test
+    public void testPersistConfigurationCollection_Nested() throws Exception {
+        ConfigurationPersistenceStrategy2 underTest = 
context.registerInjectActivateService(new 
DefaultConfigurationPersistenceStrategy());
+        
+        // store new config collection items
+        
assertTrue(underTest.persistConfigurationCollection(context.resourceResolver(), 
"/conf/test",
+                new ConfigurationCollectionPersistData(ImmutableList.of(
+                        new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value1")).collectionItemName("item1"),
+                        new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value2")).collectionItemName("item2")
+                ))
+        ));
+
+        // store nested items
+        
assertTrue(underTest.persistConfigurationCollection(context.resourceResolver(), 
"/conf/test/item1",
+                new ConfigurationCollectionPersistData(ImmutableList.of(
+                        new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value11")).collectionItemName("sub1"),
+                        new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value12")).collectionItemName("sub2")
+                ))
+        ));
+        
assertTrue(underTest.persistConfigurationCollection(context.resourceResolver(), 
"/conf/test/item2",
+                new ConfigurationCollectionPersistData(ImmutableList.of(
+                        new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value21")).collectionItemName("sub1")
+                ))
+        ));
+        
+        context.resourceResolver().commit();
+
+        
+        Resource resource = 
context.resourceResolver().getResource("/conf/test");
+        assertThat(resource, ResourceMatchers.containsChildren("item1", 
"item2"));
+
+        assertThat(resource.getChild("item1"), ResourceMatchers.props("prop1", 
"value1"));
+        assertThat(resource.getChild("item1"), 
ResourceMatchers.containsChildren("sub1", "sub2"));
+        assertThat(resource.getChild("item1/sub1"), 
ResourceMatchers.props("prop1", "value11"));
+        assertThat(resource.getChild("item1/sub2"), 
ResourceMatchers.props("prop1", "value12"));
+        
+        assertThat(resource.getChild("item2"), ResourceMatchers.props("prop1", 
"value2"));
+        assertThat(resource.getChild("item2"), 
ResourceMatchers.containsChildren("sub1"));
+        assertThat(resource.getChild("item2/sub1"), 
ResourceMatchers.props("prop1", "value21"));
+
+
+        // update config collection items
+        
assertTrue(underTest.persistConfigurationCollection(context.resourceResolver(), 
"/conf/test",
+                new ConfigurationCollectionPersistData(ImmutableList.of(
+                        new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value2-new")).collectionItemName("item2"),
+                        new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value1-new")).collectionItemName("item1"),
+                        new 
ConfigurationPersistData(ImmutableMap.<String,Object>of("prop1", 
"value3-new")).collectionItemName("item3")
+                ))
+        ));
+        context.resourceResolver().commit();
+        
+        resource = context.resourceResolver().getResource("/conf/test");
+        assertThat(resource, ResourceMatchers.containsChildren("item1", 
"item2", "item3"));
+
+        assertThat(resource.getChild("item1"), ResourceMatchers.props("prop1", 
"value1-new"));
+        assertThat(resource.getChild("item1"), 
ResourceMatchers.containsChildren("sub1", "sub2"));
+        assertThat(resource.getChild("item1/sub1"), 
ResourceMatchers.props("prop1", "value11"));
+        assertThat(resource.getChild("item1/sub2"), 
ResourceMatchers.props("prop1", "value12"));
+        
+        assertThat(resource.getChild("item2"), ResourceMatchers.props("prop1", 
"value2-new"));
+        assertThat(resource.getChild("item2"), 
ResourceMatchers.containsChildren("sub1"));
+        assertThat(resource.getChild("item2/sub1"), 
ResourceMatchers.props("prop1", "value21"));
+
+        assertThat(resource.getChild("item3"), ResourceMatchers.props("prop1", 
"value3-new"));
+        assertFalse(resource.getChild("item3").listChildren().hasNext());
+
 
         // remove config collection items
         
assertTrue(underTest.persistConfigurationCollection(context.resourceResolver(), 
"/conf/test",


Reply via email to