You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2020/07/03 15:17:21 UTC

[jackrabbit-filevault] branch master updated: JCRVLT-449 various fixes for RCP task serialization/deserialization

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

kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git


The following commit(s) were added to refs/heads/master by this push:
     new 917660d  JCRVLT-449 various fixes for RCP task serialization/deserialization
917660d is described below

commit 917660d0076b8be0a11b4f226dde798842128646
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Fri Jul 3 17:17:10 2020 +0200

    JCRVLT-449 various fixes for RCP task serialization/deserialization
---
 .../vault/rcp/impl/RcpTaskManagerImpl.java         | 62 ++++++++++++++++------
 .../vault/rcp/impl/RcpTaskManagerImplTest.java     | 12 +++--
 2 files changed, 54 insertions(+), 20 deletions(-)

diff --git a/vault-rcp/src/main/java/org/apache/jackrabbit/vault/rcp/impl/RcpTaskManagerImpl.java b/vault-rcp/src/main/java/org/apache/jackrabbit/vault/rcp/impl/RcpTaskManagerImpl.java
index 03b942e..74caf67 100644
--- a/vault-rcp/src/main/java/org/apache/jackrabbit/vault/rcp/impl/RcpTaskManagerImpl.java
+++ b/vault-rcp/src/main/java/org/apache/jackrabbit/vault/rcp/impl/RcpTaskManagerImpl.java
@@ -28,6 +28,8 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.SortedMap;
 import java.util.TreeMap;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import javax.jcr.Credentials;
 import javax.jcr.RepositoryException;
@@ -46,6 +48,7 @@ import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
 import org.osgi.service.component.annotations.Reference;
 import org.osgi.service.component.propertytypes.ServiceVendor;
 import org.osgi.service.metatype.annotations.AttributeDefinition;
@@ -90,10 +93,12 @@ public class RcpTaskManagerImpl implements RcpTaskManager {
     
     private final Configuration configuration;
 
+    /** the serialized tasks which have been processed (for detecting relevant updates) */
+    private String serializedTasks;
 
     @Activate
     public RcpTaskManagerImpl(BundleContext bundleContext, @Reference DynamicClassLoaderManager dynLoaderMgr,
-            @Reference ConfigurationAdmin configurationAdmin) {
+            @Reference ConfigurationAdmin configurationAdmin, Map <String, Object> newConfigProperties) {
         mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
         mapper.addMixIn(RepositoryAddress.class, RepositoryAddressMixin.class);
         SimpleModule module = new SimpleModule();
@@ -107,7 +112,7 @@ public class RcpTaskManagerImpl implements RcpTaskManager {
         Configuration configuration;
         try {
             configuration = configurationAdmin.getConfiguration(PID);
-            tasks = loadTasks(configuration.getProperties(), dataFile);
+            tasks = loadTasks((String)newConfigProperties.get(PROP_TASKS_SERIALIZATION), dataFile);
         } catch (IOException e) {
             log.error("Could not restore previous tasks", e);
             configuration = null;
@@ -123,38 +128,53 @@ public class RcpTaskManagerImpl implements RcpTaskManager {
         }
         log.info("RcpTaskManager deactivated. Stopping running tasks...done.");
     }
-    
-    private void persistTasks() {
-        Dictionary<String, Object> configProperties = new Hashtable<>();
-        try {
-            persistTasks(configProperties, dataFile);
-            configuration.update(configProperties);
-            log.info("Persisted RCP tasks in OSGi configuration");
-        } catch (RepositoryException | IOException e) {
-            throw new IllegalStateException("Could not persist tasks", e);
+
+    @Modified
+    void modified(Map <String, Object> newConfigProperties) throws IOException {
+        // might be triggered internally or externally
+        // only external events are relevant
+        if (!serializedTasks.equals(newConfigProperties.get(PROP_TASKS_SERIALIZATION))) {
+            log.info("Detected external properties change");
+            tasks = loadTasks((String) newConfigProperties.get(PROP_TASKS_SERIALIZATION), null);
         }
     }
 
-    private SortedMap<String, RcpTaskImpl> loadTasks(Dictionary<String, Object> configProperties, File dataFile) throws IOException {
-        if (configProperties == null) {
+    static Map<String, Object> createMapFromDictionary(Dictionary<String, Object> dictionary) {
+        // filter out irrelevant properties
+        List<String> keys = Collections.list(dictionary.keys());
+        return keys.stream().collect(Collectors.toMap(Function.identity(), dictionary::get));
+    }
+
+    private SortedMap<String, RcpTaskImpl> loadTasks(String serializedTasks, File dataFile) throws IOException {
+        if (serializedTasks != null && serializedTasks.isEmpty()) {
             log.info("No previously persisted tasks found in OSGi configuation");
             return new TreeMap<>();
         }
-        String serializedTasks = (String) configProperties.get(PROP_TASKS_SERIALIZATION);
         if (serializedTasks == null) {
             log.info("No previously persisted tasks found in OSGi configuation");
             return new TreeMap<>();
         }
         SortedMap<String, RcpTaskImpl> tasks = mapper.readValue(serializedTasks, new TypeReference<SortedMap<String, RcpTaskImpl>>() {});
+        validateTasks(tasks);
         // additionally load credentials data from bundle context
         if (dataFile != null && dataFile.exists()) {
             loadTasksCredentials(tasks, dataFile);
         } else {
             log.info("No previously persisted task credentials found at '{}'", dataFile);
         }
+        this.serializedTasks = serializedTasks;
         return tasks;
     }
 
+    void validateTasks(SortedMap<String, RcpTaskImpl> tasks) {
+        for (Map.Entry<String, RcpTaskImpl> entry : tasks.entrySet()) {
+            // make sure id of map entry is task id
+            if (!entry.getKey().equals(entry.getValue().getId())) {
+                throw new IllegalArgumentException("Id of entry " + entry.getKey() + " does not match its task id " + entry.getValue().getId());
+            }
+        }
+    }
+
     private void loadTasksCredentials(Map<String, RcpTaskImpl> tasks, File dataFile) throws IOException {
         Properties props = new Properties();
         try (FileInputStream inputStream = new FileInputStream(dataFile)) {
@@ -169,8 +189,19 @@ public class RcpTaskManagerImpl implements RcpTaskManager {
         }
     }
 
+    private void persistTasks() {
+        Dictionary<String, Object> configProperties = new Hashtable<>();
+        try {
+            persistTasks(configProperties, dataFile);
+            configuration.updateIfDifferent(configProperties);
+            log.info("Persisted RCP tasks in OSGi configuration");
+        } catch (RepositoryException | IOException e) {
+            throw new IllegalStateException("Could not persist tasks", e);
+        }
+    }
+
     private void persistTasks(Dictionary<String, Object> configProperties, File dataFile) throws RepositoryException, JsonGenerationException, JsonMappingException, IOException {
-        String serializedTasks = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(tasks);
+        serializedTasks = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(tasks);
         configProperties.put(PROP_TASKS_SERIALIZATION, serializedTasks);
 
         // additionally persist the sensitive data in a data file
@@ -229,6 +260,7 @@ public class RcpTaskManagerImpl implements RcpTaskManager {
         RcpTask rcpTask = tasks.remove(taskId);
         if (rcpTask != null) {
             rcpTask.stop();
+            persistTasks();
             return true;
         }
         return false;
diff --git a/vault-rcp/src/test/java/org/apache/jackrabbit/vault/rcp/impl/RcpTaskManagerImplTest.java b/vault-rcp/src/test/java/org/apache/jackrabbit/vault/rcp/impl/RcpTaskManagerImplTest.java
index 1c27341..68b919d 100644
--- a/vault-rcp/src/test/java/org/apache/jackrabbit/vault/rcp/impl/RcpTaskManagerImplTest.java
+++ b/vault-rcp/src/test/java/org/apache/jackrabbit/vault/rcp/impl/RcpTaskManagerImplTest.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
 import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.stream.Collectors;
 
@@ -71,7 +72,7 @@ public class RcpTaskManagerImplTest {
     @Mock
     Configuration mockConfiguration;
 
-    Dictionary configProperties;
+    Dictionary<String, Object> configProperties;
 
     @Rule
     public TemporaryFolder folder= new TemporaryFolder();
@@ -95,8 +96,8 @@ public class RcpTaskManagerImplTest {
                 return null;
             }
             
-        }).when(mockConfiguration).update(Mockito.any());
-        RcpTaskManagerImpl taskManager = new RcpTaskManagerImpl(mockBundleContext, mockClassLoaderManager, mockConfigurationAdmin);
+        }).when(mockConfiguration).updateIfDifferent(Mockito.any());
+        RcpTaskManagerImpl taskManager = new RcpTaskManagerImpl(mockBundleContext, mockClassLoaderManager, mockConfigurationAdmin, Collections.emptyMap());
         DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
         try (InputStream input = this.getClass().getResourceAsStream("/filter.xml")) {
             filter.load(input);
@@ -104,8 +105,9 @@ public class RcpTaskManagerImplTest {
         taskManager.addTask(new RepositoryAddress("http://localhost:4502"), new SimpleCredentials("testUser", "pw".toCharArray()), "/target/path", "2", Arrays.asList("exclude1", "exclude2"), false);
         taskManager.addTask(new RepositoryAddress("http://localhost:8080"), new SimpleCredentials("testUser3", "pw3".toCharArray()), "/target/path5", "3", filter, true);
         taskManager.deactivate();
-        Mockito.when(mockConfiguration.getProperties()).thenReturn(configProperties);
-        RcpTaskManagerImpl taskManager2 = new RcpTaskManagerImpl(mockBundleContext, mockClassLoaderManager, mockConfigurationAdmin);
+        Assert.assertNotNull("The tasks should have been persisted here but are not!", configProperties);
+        // convert to Map
+        RcpTaskManagerImpl taskManager2 = new RcpTaskManagerImpl(mockBundleContext, mockClassLoaderManager, mockConfigurationAdmin, RcpTaskManagerImpl.createMapFromDictionary(configProperties));
         // how to get list ordered by id?
         Assert.assertThat(taskManager.tasks.values(), new TaskCollectionMatcher(taskManager2.tasks.values()));
     }