[ 
https://issues.apache.org/jira/browse/KARAF-5562?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16640973#comment-16640973
 ] 

ASF GitHub Bot commented on KARAF-5562:
---------------------------------------

jbonofre closed pull request #44: [KARAF-5562] Improve cellar groups 
configuration synchronisation from…
URL: https://github.com/apache/karaf-cellar/pull/44
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/hazelcast/src/main/java/org/apache/karaf/cellar/hazelcast/HazelcastGroupManager.java
 
b/hazelcast/src/main/java/org/apache/karaf/cellar/hazelcast/HazelcastGroupManager.java
index 96b923dc..835b5476 100644
--- 
a/hazelcast/src/main/java/org/apache/karaf/cellar/hazelcast/HazelcastGroupManager.java
+++ 
b/hazelcast/src/main/java/org/apache/karaf/cellar/hazelcast/HazelcastGroupManager.java
@@ -13,15 +13,8 @@
  */
 package org.apache.karaf.cellar.hazelcast;
 
-import java.io.IOException;
-import java.util.*;
-
 import com.hazelcast.core.*;
-import org.apache.karaf.cellar.core.Configurations;
-import org.apache.karaf.cellar.core.Group;
-import org.apache.karaf.cellar.core.GroupManager;
-import org.apache.karaf.cellar.core.Node;
-import org.apache.karaf.cellar.core.Synchronizer;
+import org.apache.karaf.cellar.core.*;
 import org.apache.karaf.cellar.core.event.EventConsumer;
 import org.apache.karaf.cellar.core.event.EventProducer;
 import org.apache.karaf.cellar.core.event.EventTransportFactory;
@@ -36,22 +29,26 @@
 import org.osgi.service.cm.ConfigurationListener;
 import org.slf4j.Logger;
 
+import java.io.IOException;
+import java.util.*;
+
 /**
  * A group manager implementation powered by Hazelcast.
  * The role of this class is to provide means of creating groups, setting 
nodes to groups etc.
  * Keep in sync the distributed group configuration with the locally persisted.
  */
-public class HazelcastGroupManager implements GroupManager, EntryListener, 
ConfigurationListener {
+public class HazelcastGroupManager implements GroupManager, 
EntryListener<String,Object>, ConfigurationListener {
 
     private static final transient Logger LOGGER = 
org.slf4j.LoggerFactory.getLogger(HazelcastGroupManager.class);
 
-    private static final String GROUPS = "org.apache.karaf.cellar.groups";
-    private static final String GROUPS_CONFIG = 
"org.apache.karaf.cellar.groups.config";
-    private static final String DEFAULT_GROUP = "default";
+    private static final String HAZELCAST_GROUPS = 
"org.apache.karaf.cellar.groups";
+    private static final String HAZELCAST_GROUPS_CONFIG = 
"org.apache.karaf.cellar.groups.config";
 
     private Map<String, ServiceRegistration> producerRegistrations = new 
HashMap<String, ServiceRegistration>();
     private Map<String, ServiceRegistration> consumerRegistrations = new 
HashMap<String, ServiceRegistration>();
 
+    private Map<String, Object> localConfig = new HashMap<String, Object>();
+
     private Map<String, EventProducer> groupProducers = new HashMap<String, 
EventProducer>();
     private Map<String, EventConsumer> groupConsumer = new HashMap<String, 
EventConsumer>();
 
@@ -64,47 +61,140 @@
     private CombinedClassLoader combinedClassLoader;
 
     public void init() {
-        // create a listener for group configuration.
-        IMap groupConfiguration = instance.getMap(GROUPS_CONFIG);
-        groupConfiguration.addEntryListener(this, true);
         try {
             // create group stored in configuration admin
-            Configuration configuration = 
configurationAdmin.getConfiguration(Configurations.GROUP, null);
-            if (configuration != null) {
-                Dictionary<String, Object> properties = 
configuration.getProperties();
-                if (properties == null) {
+            Configuration groupsConfiguration = getConfigurationForGroups();
+            Dictionary<String, Object> properties = 
groupsConfiguration.getProperties();
+
+            // create a listener for group configuration.
+            IMap<String,Object> hazelcastGroupsConfig = 
getClusterGroupsConfig();
+
+            hazelcastGroupsConfig.addEntryListener(this, true);
+
+            if (hazelcastGroupsConfig.isEmpty()) {
+                // First one to be here - initialize hazelcast map with local 
configuration
+                LOGGER.debug("CELLAR HAZELCAST: intialize cluster with local 
config");
+
+                Map<String, Object> updates = 
getUpdatesForHazelcastMap(properties);
+                hazelcastGroupsConfig.putAll(updates);
+            } else {
+                if (properties != null) {
+                    Enumeration<String> en = properties.keys();
+                    while (en.hasMoreElements()) {
+                        String key = en.nextElement();
+                        localConfig.put(key , properties.get(key));
+                    }
+                } else {
                     properties = new Hashtable<String, Object>();
                 }
-                String groups = (String) 
properties.get(Configurations.GROUPS_KEY);
-                Set<String> groupNames = convertStringToSet(groups);
-                if (groupNames != null && !groupNames.isEmpty()) {
-                    for (String groupName : groupNames) {
-                        createGroup(groupName);
-                    }
+                boolean updated = false;
+                for (String key : hazelcastGroupsConfig.keySet()) {
+                    Object value = hazelcastGroupsConfig.get(key);
+                    updated |= updatePropertiesFromHazelcastMap(properties, 
key, value);
+                }
+                if (updated) {
+                    updateConfiguration(groupsConfiguration, properties);
                 }
             }
+
+            Node node = getNode();
+
+            // add group membership from configuration
+            properties = getConfigurationForNode().getProperties();
+            Set<String> groupNames = convertStringToSet(properties != null ? 
(String) properties.get(Configurations.GROUPS_KEY) : null);
+            getClusterGroups().put(node, groupNames);
         } catch (IOException e) {
             LOGGER.warn("CELLAR HAZELCAST: can't create cluster group from 
configuration admin", e);
         }
-        try {
-            // add group membership from configuration
-            Configuration configuration = 
configurationAdmin.getConfiguration(Configurations.NODE, null);
-            if (configuration != null) {
-                Dictionary<String, Object> properties = 
configuration.getProperties();
-                if (properties == null) {
-                    properties = new Hashtable<String, Object>();
+    }
+
+    private boolean updatePropertiesFromHazelcastMap(Dictionary<String, 
Object> properties, String key, Object value) {
+        if (!(value instanceof Map)) {
+            return false;
+        }
+        boolean changed = false;
+        @SuppressWarnings("unchecked")
+        Map<String, Object> map = (Map<String, Object>) value;
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
+            String entryKey = entry.getKey();
+            Object entryValue = entry.getValue();
+            if (entryKey.equals(".change")) {
+                Set<String> groups = convertStringToSet((String) 
properties.get(Configurations.GROUPS_KEY));
+                if ((entryValue.equals("added") && !groups.contains(key))
+                        || entryValue.equals("removed") && 
groups.contains(key)) {
+                    LOGGER.debug("CELLAR HAZELCAST: get group " + key + " 
configuration from cluster : " + key
+                            + " has been " + entryValue);
+                    if (entryValue.equals("added")) {
+                        groups.add(key);
+                    } else {
+                        groups.remove(key);
+                    }
+                    String newValue = convertSetToString(groups);
+                    properties.put(Configurations.GROUPS_KEY, newValue);
+                    localConfig.put(Configurations.GROUPS_KEY, newValue);
+                    changed = true;
                 }
-                String groups = (String) 
properties.get(Configurations.GROUPS_KEY);
-                Set<String> groupNames = convertStringToSet(groups);
-                if (groupNames != null && !groupNames.isEmpty()) {
-                    for (String groupName : groupNames) {
-                        registerGroup(groupName);
+            } else if (properties.get(entryKey) == null || 
!properties.get(entryKey).equals(entryValue)) {
+                LOGGER.debug("CELLAR HAZELCAST: get group " + key + " 
configuration from cluster : " + entryKey + " = "
+                        + entryValue);
+                properties.put(entryKey, entryValue);
+                localConfig.put(entryKey, entryValue);
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    private Map<String, Object> getUpdatesForHazelcastMap(Dictionary<String, 
Object> properties) {
+        Map<String,Object> updates = new HashMap<String,Object>();
+        if (properties == null) {
+            return updates;
+        }
+        Enumeration<String> en = properties.keys();
+        while (en.hasMoreElements()) {
+            String key = en.nextElement();
+            Object value = properties.get(key);
+
+            if (!key.startsWith("felix.") && !key.startsWith("service.")) {
+                Object localValue = localConfig.get(key);
+                if (localValue == null || !localValue.equals(value)) {
+                    if (key.equals(Configurations.GROUPS_KEY)) {
+                        Set<String> removedGroups = 
convertStringToSet((String) localValue);
+                        Set<String> addedGroups = convertStringToSet((String) 
value);
+                        addedGroups.removeAll(removedGroups);
+                        removedGroups.removeAll(convertStringToSet((String) 
value));
+                        for (String addedGroup : addedGroups) {
+                            getOrCreateMap(updates, addedGroup).put(".change", 
"added");
+                        }
+                        for (String removedGroup : removedGroups) {
+                            getOrCreateMap(updates, 
removedGroup).put(".change", "removed");
+                        }
+                    } else {
+                        int idx = key.indexOf(".");
+                        if (idx > 0) {
+                            String groupKey = key.substring(0, idx);
+                            getOrCreateMap(updates, groupKey).put(key, value);
+                        } else {
+                            LOGGER.warn("CELLAR HAZELCAST: found group 
property that is not prefixed with a group name: {}. Skipping it", key); 
+                        }
                     }
                 }
             }
-        } catch (IOException e) {
-            LOGGER.error("CELLAR HAZELCAST: can't set group membership for the 
current node", e);
+
+            localConfig.put(key , value);
+
         }
+        return updates;
+    }
+
+    private Map<String, Object> getOrCreateMap(Map<String, Object> updates, 
String group) {
+        @SuppressWarnings("unchecked")
+        Map<String, Object> props = (Map<String, Object>) updates.get(group);
+        if (props == null) {
+            props = new HashMap<String, Object>();
+            updates.put(group, props);
+        }
+        return props;
     }
 
     public void destroy() {
@@ -113,12 +203,9 @@ public void destroy() {
             Thread.currentThread().setContextClassLoader(combinedClassLoader);
             // update the group
             Node local = this.getNode();
-            Set<Group> groups = this.listGroups(local);
-            for (Group group : groups) {
-                String groupName = group.getName();
-                group.getNodes().remove(local);
-                listGroups().put(groupName, group);
-            }
+
+            getClusterGroups().remove(local);
+
             // shutdown the group consumer/producers
             for (Map.Entry<String, EventConsumer> consumerEntry : 
groupConsumer.entrySet()) {
                 EventConsumer consumer = consumerEntry.getValue();
@@ -153,18 +240,24 @@ public Group createGroup(String groupName) {
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
         try {
             Thread.currentThread().setContextClassLoader(combinedClassLoader);
-            Group group = listGroups().get(groupName);
+            Map<String, Group> listGroups = listGroups();
+            Group group = listGroups.get(groupName);
             if (group == null) {
                 group = new Group(groupName);
-            }
-            if (!listGroups().containsKey(groupName)) {
-                copyGroupConfiguration(Configurations.DEFAULT_GROUP_NAME, 
groupName);
-                listGroups().put(groupName, group);
                 try {
-                    // store the group list to configuration admin
-                    persist(listGroups());
-                } catch (Exception e) {
-                    LOGGER.warn("CELLAR HAZELCAST: can't store cluster group 
list", e);
+                    Configuration configuration = getConfigurationForGroups();
+                    if (configuration != null) {
+                        Dictionary<String, Object> properties = 
configuration.getProperties();
+                        if (properties != null && !properties.isEmpty()) {
+                            properties = 
copyGroupConfiguration(Configurations.DEFAULT_GROUP_NAME + '.', groupName + 
'.', properties);
+                            Set<String> groups = convertStringToSet((String) 
properties.get(Configurations.GROUPS_KEY));
+                            groups.add(groupName);
+                            properties.put(Configurations.GROUPS_KEY, 
convertSetToString(groups));
+                            updateConfiguration(configuration, properties);
+                        }
+                    }
+                } catch (IOException e) {
+                    LOGGER.error("CELLAR HAZELCAST: failed to update cluster 
group configuration", e);
                 }
             }
             return group;
@@ -179,11 +272,15 @@ public void deleteGroup(String groupName) {
         try {
             Thread.currentThread().setContextClassLoader(combinedClassLoader);
             if (!groupName.equals(Configurations.DEFAULT_GROUP_NAME)) {
-                listGroups().remove(groupName);
                 try {
                     // store the group list to configuration admin
-                    persist(listGroups());
-                } catch (Exception e) {
+                    Configuration configuration = getConfigurationForGroups();
+                    Dictionary<String, Object> properties = 
configuration.getProperties();
+                    Set<String> groups = convertStringToSet((String) 
properties.get(Configurations.GROUPS_KEY));
+                    groups.remove(groupName);
+                    properties.put(Configurations.GROUPS_KEY, 
convertSetToString(groups));
+                    updateConfiguration(configuration, properties);
+                } catch (IOException e) {
                     LOGGER.warn("CELLAR HAZELCAST: can't store cluster group 
list", e);
                 }
             }
@@ -192,24 +289,6 @@ public void deleteGroup(String groupName) {
         }
     }
 
-    /**
-     * Store the group names in configuration admin.
-     *
-     * @param groups the list of group to store.
-     * @throws Exception in case of storage failure.
-     */
-    private void persist(Map<String, Group> groups) throws Exception {
-        Configuration configuration = 
configurationAdmin.getConfiguration(Configurations.GROUP, null);
-        if (configuration != null) {
-            Dictionary<String, Object> properties = 
configuration.getProperties();
-            if (properties == null) {
-                properties = new Hashtable<String, Object>();
-            }
-            properties.put(Configurations.GROUPS_KEY, 
convertSetToString(groups.keySet()));
-            configuration.update(properties);
-        }
-    }
-
     @Override
     public Set<Group> listLocalGroups() {
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
@@ -264,8 +343,24 @@ public Group findGroupByName(String groupName) {
     public Map<String, Group> listGroups() {
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
         try {
+            Map<String, Group> res = new HashMap<String, Group>();
             Thread.currentThread().setContextClassLoader(combinedClassLoader);
-            return instance.getMap(GROUPS);
+            Map<Node, Set<String>> nodes = getClusterGroups();
+
+            Set<String> groups = convertStringToSet((String) 
localConfig.get(Configurations.GROUPS_KEY));
+            groups.add(Configurations.DEFAULT_GROUP_NAME);
+
+            for (String groupName : groups) {
+                Group group = new Group(groupName);
+                res.put(groupName, group);
+                for (Map.Entry<Node, Set<String>> entry : nodes.entrySet()) {
+                    if (entry.getValue().contains(groupName)) {
+                        group.getNodes().add(entry.getKey());
+                    }
+                }
+            }
+
+            return res;
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
@@ -278,15 +373,15 @@ public Group findGroupByName(String groupName) {
             Thread.currentThread().setContextClassLoader(combinedClassLoader);
             Set<Group> result = new HashSet<Group>();
 
-            Map<String, Group> groupMap = instance.getMap(GROUPS);
-            Collection<Group> groupCollection = groupMap.values();
-            if (groupCollection != null && !groupCollection.isEmpty()) {
-                for (Group group : groupCollection) {
-                    if (group.getNodes().contains(node)) {
-                        result.add(group);
-                    }
-                }
+            Map<Node, Set<String>> groupMap = getClusterGroups();
+            Set<String> groupNames = groupMap.get(node);
+
+            if (groupNames != null) {
+                Map<String, Group> g = listGroups();
+                g.keySet().retainAll(groupNames);
+                result.addAll(g.values());
             }
+
             return result;
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
@@ -336,7 +431,6 @@ public void registerGroup(Group group) {
         try {
             Thread.currentThread().setContextClassLoader(combinedClassLoader);
             String groupName = group.getName();
-            createGroup(groupName);
 
             LOGGER.debug("CELLAR HAZELCAST: registering cluster group {}.", 
groupName);
             Properties serviceProperties = new Properties();
@@ -366,12 +460,17 @@ public void registerGroup(Group group) {
                 consumerRegistrations.put(groupName, consumerRegistration);
             }
 
-            group.getNodes().add(getNode());
-            listGroups().put(groupName, group);
+            Node node = getNode();
+            group.getNodes().add(node);
+            Map<Node, Set<String>> map = getClusterGroups();
+            Set<String> groupNames = (Set<String>) map.get(node);
+            groupNames = new HashSet<String>(groupNames);
+            groupNames.add(groupName);
+            map.put(node, groupNames);
 
             // add group to configuration
             try {
-                Configuration configuration = 
configurationAdmin.getConfiguration(Configurations.NODE, null);
+                Configuration configuration = getConfigurationForNode();
                 if (configuration != null) {
                     Dictionary<String, Object> properties = 
configuration.getProperties();
                     if (properties != null) {
@@ -388,7 +487,7 @@ public void registerGroup(Group group) {
                             groups = groupName;
                         }
                         properties.put(Configurations.GROUPS_KEY, groups);
-                        configuration.update(properties);
+                        updateConfiguration(configuration, properties);
                     }
                 }
             } catch (IOException e) {
@@ -420,11 +519,7 @@ public void registerGroup(String groupName) {
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
         try {
             Thread.currentThread().setContextClassLoader(combinedClassLoader);
-            Group group = listGroups().get(groupName);
-            if (group == null) {
-                group = new Group(groupName);
-            }
-            registerGroup(group);
+            registerGroup(new Group(groupName));
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
@@ -476,9 +571,17 @@ public void unRegisterGroup(Group group) {
                 consumer.stop();
             }
 
+            Node node = getNode();
+            group.getNodes().add(node);
+            Map<Node, Set<String>> map = getClusterGroups();
+            Set<String> groupNames = (Set<String>) map.get(node);
+            groupNames = new HashSet<String>(groupNames);
+            groupNames.remove(groupName);
+            map.put(node, groupNames);
+
             // remove cluster group from configuration
             try {
-                Configuration configuration = 
configurationAdmin.getConfiguration(Configurations.NODE, null);
+                Configuration configuration = getConfigurationForNode();
                 Dictionary<String, Object> properties = 
configuration.getProperties();
                 String groups = (String) 
properties.get(Configurations.GROUPS_KEY);
                 if (groups == null || groups.isEmpty()) {
@@ -489,7 +592,7 @@ public void unRegisterGroup(Group group) {
                     groups = convertSetToString(groupNamesSet);
                 }
                 properties.put(Configurations.GROUPS_KEY, groups);
-                configuration.update(properties);
+                updateConfiguration(configuration, properties);
             } catch (IOException e) {
                 LOGGER.error("CELLAR HAZELCAST: failed to read cluster group 
configuration", e);
             }
@@ -501,50 +604,24 @@ public void unRegisterGroup(Group group) {
     /**
      * Copy the configuration of a cluster {@link Group}.
      *
-     * <b>1.</b> Updates configuration admin from Hazelcast using source 
config.
-     * <b>2.</b> Creates target configuration both on Hazelcast and 
configuration admin.
-     *
-     * @param sourceGroupName the source cluster group.
-     * @param targetGroupName the target cluster group.
+     * @param sourceGroupPrefix the source cluster group prefix
+     * @param targetGroupPrefix the target cluster group prefix
+     * @return new properties object
      */
-    public void copyGroupConfiguration(String sourceGroupName, String 
targetGroupName) {
-        try {
-            Configuration conf = 
configurationAdmin.getConfiguration(Configurations.GROUP, null);
-            if (conf != null) {
-
-                // get configuration from config admin
-                Dictionary configAdminProperties = conf.getProperties();
-                if (configAdminProperties == null) {
-                    configAdminProperties = new Properties();
-                }
-                // get configuration from Hazelcast
-                Map<String, String> sourceGroupConfig = 
instance.getMap(GROUPS_CONFIG);
-
-                // update local configuration from cluster
-                for (Map.Entry<String, String> parentEntry : 
sourceGroupConfig.entrySet()) {
-                    configAdminProperties.put(parentEntry.getKey(), 
parentEntry.getValue());
-                }
-
-                Dictionary updatedProperties = new Properties();
-                Enumeration keyEnumeration = configAdminProperties.keys();
-                while (keyEnumeration.hasMoreElements()) {
-                    String key = (String) keyEnumeration.nextElement();
-                    String value = configAdminProperties.get(key).toString();
-
-                    if (key.startsWith(sourceGroupName)) {
-                        String newKey = key.replace(sourceGroupName, 
targetGroupName);
-                        updatedProperties.put(newKey, value);
-                        sourceGroupConfig.put(key, value);
-                    }
-                    updatedProperties.put(key, value);
-                }
-
-                conf.update(updatedProperties);
+    public Dictionary<String, Object> copyGroupConfiguration(String 
sourceGroupPrefix, String targetGroupPrefix, Dictionary<String, Object> 
properties) {
+        Dictionary<String, Object> updatedProperties = new Hashtable<String, 
Object>();
+        Enumeration<String> keyEnumeration = properties.keys();
+        while (keyEnumeration.hasMoreElements()) {
+            String key = keyEnumeration.nextElement();
+            Object value = properties.get(key);
+
+            if (key.startsWith(sourceGroupPrefix)) {
+                String newKey = targetGroupPrefix + 
key.substring(sourceGroupPrefix.length());
+                updatedProperties.put(newKey, value);
             }
-
-        } catch (IOException e) {
-            LOGGER.error("CELLAR HAZELCAST: failed to read cluster group 
configuration", e);
+            updatedProperties.put(key, value);
         }
+        return updatedProperties;
     }
 
     /**
@@ -554,7 +631,7 @@ public void copyGroupConfiguration(String sourceGroupName, 
String targetGroupNam
      * @return the String corresponding to the Set.
      */
     protected String convertSetToString(Set<String> set) {
-        StringBuffer result = new StringBuffer();
+        StringBuilder result = new StringBuilder();
         Iterator<String> groupIterator = set.iterator();
         while (groupIterator.hasNext()) {
             String name = groupIterator.next();
@@ -573,14 +650,14 @@ protected String convertSetToString(Set<String> set) {
      * @return the Set corresponding to the String.
      */
     protected Set<String> convertStringToSet(String string) {
-       if (string == null)
-               return Collections.EMPTY_SET;
-        Set<String> result = new HashSet<String>();
+        if (string == null)
+            return Collections.emptySet();
+        Set<String> result = new TreeSet<String>();
         String[] groupNames = string.split(",");
 
         if (groupNames != null && groupNames.length > 0) {
             for (String name : groupNames) {
-                result.add(name);
+                result.add(name.trim());
             }
         } else {
             result.add(string);
@@ -593,26 +670,35 @@ protected String convertSetToString(Set<String> set) {
      *
      * @param configurationEvent the local configuration event.
      */
+    @SuppressWarnings("unchecked")
     @Override
     public void configurationEvent(ConfigurationEvent configurationEvent) {
-        String pid = configurationEvent.getPid();
-        if (pid.equals(GROUPS)) {
-            Map groupConfiguration = instance.getMap(GROUPS_CONFIG);
-
-            try {
-                Configuration conf = 
configurationAdmin.getConfiguration(GROUPS, null);
-                Dictionary properties = conf.getProperties();
-                Enumeration keyEnumeration = properties.keys();
-                while (keyEnumeration.hasMoreElements()) {
-                    Object key = keyEnumeration.nextElement();
-                    Object value = properties.get(key);
-                    if (!groupConfiguration.containsKey(key) || 
groupConfiguration.get(key) == null || 
!groupConfiguration.get(key).equals(value)) {
-                        groupConfiguration.put(key, value);
+        if (!Configurations.GROUP.equals(configurationEvent.getPid())) {
+            return;
+        }
+        try {
+            Map<String, Object> hazelcastGroupConfig = 
getClusterGroupsConfig();
+            Configuration conf = getConfigurationForGroups();
+            Dictionary<String, Object> properties = conf.getProperties();
+            Map<String, Object> updates = 
getUpdatesForHazelcastMap(properties);
+            for (Map.Entry<String, Object> entry : updates.entrySet()) {
+                String key = entry.getKey();
+                Object value = entry.getValue();
+                Object clusterValue = hazelcastGroupConfig.get(key);
+                if (clusterValue == null || !clusterValue.equals(value)) {
+                    LOGGER.debug("CELLAR HAZELCAST : sending updates to 
cluster : " + key + " = " + value);
+                    if (clusterValue != null && value instanceof Map) {
+                        @SuppressWarnings("rawtypes")
+                        Map<String, Object> newValue = new HashMap((Map) 
clusterValue);
+                        newValue.putAll((Map<? extends String, ?>) value);
+                        hazelcastGroupConfig.put(key, newValue);
+                    } else {
+                        hazelcastGroupConfig.put(key, value);
                     }
                 }
-            } catch (Exception e) {
-                LOGGER.warn("CELLAR HAZELCAST: failed to update cluster group 
configuration", e);
             }
+        } catch (Exception e) {
+            LOGGER.warn("CELLAR HAZELCAST: failed to update cluster group 
configuration", e);
         }
     }
 
@@ -622,7 +708,7 @@ public void configurationEvent(ConfigurationEvent 
configurationEvent) {
      * @param entryEvent entry event
      */
     @Override
-    public void entryAdded(EntryEvent entryEvent) {
+    public void entryAdded(EntryEvent<String,Object> entryEvent) {
         entryUpdated(entryEvent);
     }
 
@@ -632,7 +718,7 @@ public void entryAdded(EntryEvent entryEvent) {
      * @param entryEvent entry event
      */
     @Override
-    public void entryRemoved(EntryEvent entryEvent) {
+    public void entryRemoved(EntryEvent<String,Object> entryEvent) {
         entryUpdated(entryEvent);
     }
 
@@ -642,16 +728,16 @@ public void entryRemoved(EntryEvent entryEvent) {
      * @param entryEvent entry event
      */
     @Override
-    public void entryUpdated(EntryEvent entryEvent) {
-        LOGGER.debug("CELLAR HAZELCAST: cluster group configuration has been 
updated, updating local configuration");
+    public void entryUpdated(EntryEvent<String,Object> entryEvent) {
         try {
-            Configuration conf = configurationAdmin.getConfiguration(GROUPS, 
null);
-            Dictionary props = conf.getProperties();
-            Object key = entryEvent.getKey();
+            Configuration conf = getConfigurationForGroups();
+            Dictionary<String, Object> properties = conf.getProperties();
+            String key = entryEvent.getKey();
             Object value = entryEvent.getValue();
-            if (props.get(key) == null || !props.get(key).equals(value)) {
-                props.put(key, value);
-                conf.update(props);
+
+            if (updatePropertiesFromHazelcastMap(properties, key, value)) {
+                LOGGER.debug("CELLAR HAZELCAST: cluster group configuration 
has been updated, updating local configuration: {} = {}", key, value);
+                updateConfiguration(conf, properties);
             }
         } catch (Exception ex) {
             LOGGER.warn("CELLAR HAZELCAST: failed to update local 
configuration", ex);
@@ -664,7 +750,7 @@ public void entryUpdated(EntryEvent entryEvent) {
      * @param entryEvent entry event
      */
     @Override
-    public void entryEvicted(EntryEvent entryEvent) {
+    public void entryEvicted(EntryEvent<String,Object> entryEvent) {
         entryUpdated(entryEvent);
     }
 
@@ -718,4 +804,24 @@ public void setCombinedClassLoader(CombinedClassLoader 
combinedClassLoader) {
         this.combinedClassLoader = combinedClassLoader;
     }
 
+    private Configuration getConfigurationForGroups() throws IOException {
+        return configurationAdmin.getConfiguration(Configurations.GROUP, null);
+    }
+
+    private Configuration getConfigurationForNode() throws IOException {
+        return configurationAdmin.getConfiguration(Configurations.NODE, null);
+    }
+
+    private IMap<Node, Set<String>> getClusterGroups() {
+        return instance.getMap(HAZELCAST_GROUPS);
+    }
+
+    private IMap<String, Object> getClusterGroupsConfig() {
+        return instance.getMap(HAZELCAST_GROUPS_CONFIG);
+    }
+
+    private void updateConfiguration(Configuration cfg, Dictionary<String, 
Object> properties) throws IOException {
+        cfg.update(properties);
+        LOGGER.debug("CELLAR HAZELCAST: updated configuration with pid: {}", 
cfg.getPid());
+    }
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


> Improve cellar groups configuration synchronisation from hazelcast
> ------------------------------------------------------------------
>
>                 Key: KARAF-5562
>                 URL: https://issues.apache.org/jira/browse/KARAF-5562
>             Project: Karaf
>          Issue Type: Improvement
>          Components: cellar
>    Affects Versions: cellar-4.0.4, cellar-4.1.1
>            Reporter: Thomas Draier
>            Assignee: Jean-Baptiste Onofré
>            Priority: Major
>             Fix For: cellar-4.0.5, cellar-4.1.2, cellar-4.2.0
>
>
> We encountered different issues due to HazelcastGroupManager, I'm grouping 
> them here as all of them are linked and we fixed them in a single refactoring 
> of the class. This globally result in a better synchronization of the cellar 
> groups configuration.
> - Hazelcast network splits can result in very bad behaviour on the “groups” 
> shared map - this map contains the list of groups and its members, and the 
> system fully rely on it to know in which groups you are. If multiple nodes 
> updates the map while they are not connected together (easy to reproduce by 
> starting both nodes at the same time), and then join afterwards, the default 
> merge algorithm is applied and simply overwrite the full map. This basically 
> result in groups loosing members, even if the configuration file claims that 
> the nodes are still members. 
> - When handling the groups configuration, HazelcastGroupManager replicates 
> the felix.fileinstall.filename property on each node, containing the 
> configuration file path. It’s quite “ok” if you’re on a cluster with each 
> node installed on the exact same path - however if you’re on the same 
> machine, with 2 nodes on different paths : one node will at one point write 
> on the config file of the other node and never updates its own config, which 
> can be quite confusing.
> - The HazelcastGroupManager can start even when a configuration is not 
> detected by fileinstall yet - it then creates a new config, based on the 
> hazelcast shared config, which will override the config file when fileinstall 
> detects it. It does not have a huge impact, but it shuffles the properties 
> files and makes it unreadable. 
> - The updates from hazelcast to local config trigger back update on hazelcast 
> which goes back to local config and sometimes revert the changes, resulting 
> in no change in the config. Basically , when adding a group, a lot of 
> properties are updated - for each of them we trigger a configuration update. 
> Each configuration update triggers an event which send the whole config back 
> to hazelcast, including properties that are not updated yet, setting them 
> back to their old values. All events (hazelcast updates and osgi config) are 
> treated asynchronously - depending on the orders of events, some properties 
> can be reverted or never added (usually groups property is always reverted 
> after a group add). 



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to