You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2017/11/07 09:40:13 UTC

incubator-unomi git commit: UNOMI-136 Replace JMX statistics collecting with Karaf Cellar events - Remove JMX system statistics collection - Replace with local collection and then sending events to the Karaf Cellar cluster

Repository: incubator-unomi
Updated Branches:
  refs/heads/master 73ddec1a4 -> 7b19dfa06


UNOMI-136 Replace JMX statistics collecting with Karaf Cellar events
- Remove JMX system statistics collection
- Replace with local collection and then sending events to the Karaf Cellar cluster

Signed-off-by: Serge Huber <sh...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-unomi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-unomi/commit/7b19dfa0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-unomi/tree/7b19dfa0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-unomi/diff/7b19dfa0

Branch: refs/heads/master
Commit: 7b19dfa064b01cded88726bdc59d53b068a0b29a
Parents: 73ddec1
Author: Serge Huber <sh...@apache.org>
Authored: Tue Nov 7 10:39:45 2017 +0100
Committer: Serge Huber <sh...@apache.org>
Committed: Tue Nov 7 10:39:45 2017 +0100

----------------------------------------------------------------------
 .../services/services/ClusterServiceImpl.java   | 187 +++++++------------
 .../services/ClusterSystemStatisticsEvent.java  |  43 +++++
 .../ClusterSystemStatisticsEventHandler.java    | 138 ++++++++++++++
 .../resources/OSGI-INF/blueprint/blueprint.xml  |  23 ++-
 .../main/resources/org.apache.unomi.cluster.cfg |   9 +-
 5 files changed, 276 insertions(+), 124 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/7b19dfa0/services/src/main/java/org/apache/unomi/services/services/ClusterServiceImpl.java
----------------------------------------------------------------------
diff --git a/services/src/main/java/org/apache/unomi/services/services/ClusterServiceImpl.java b/services/src/main/java/org/apache/unomi/services/services/ClusterServiceImpl.java
index 57d8d3e..6bc0cdc 100644
--- a/services/src/main/java/org/apache/unomi/services/services/ClusterServiceImpl.java
+++ b/services/src/main/java/org/apache/unomi/services/services/ClusterServiceImpl.java
@@ -17,6 +17,7 @@
 
 package org.apache.unomi.services.services;
 
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.karaf.cellar.config.ClusterConfigurationEvent;
 import org.apache.karaf.cellar.config.Constants;
 import org.apache.karaf.cellar.core.*;
@@ -31,16 +32,12 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.management.*;
-import javax.management.remote.JMXConnector;
-import javax.management.remote.JMXConnectorFactory;
-import javax.management.remote.JMXServiceURL;
-import java.io.IOException;
+import java.io.Serializable;
 import java.lang.management.ManagementFactory;
 import java.lang.management.OperatingSystemMXBean;
 import java.lang.management.RuntimeMXBean;
-import java.net.ConnectException;
-import java.net.MalformedURLException;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Implementation of the persistence service interface
@@ -57,12 +54,13 @@ public class ClusterServiceImpl implements ClusterService {
     private GroupManager karafCellarGroupManager;
     private String karafCellarGroupName = Configurations.DEFAULT_GROUP_NAME;
     private ConfigurationAdmin osgiConfigurationAdmin;
-    private String karafJMXUsername = "karaf";
-    private String karafJMXPassword = "karaf";
-    private int karafJMXPort = 1099;
     private String publicAddress;
     private String internalAddress;
-    private Map<String, JMXConnector> jmxConnectors = new LinkedHashMap<>();
+    private Map<String, Map<String,Serializable>> nodeSystemStatistics = new ConcurrentHashMap<>();
+    private Group group = null;
+
+    private Timer nodeStatisticsUpdateTimer;
+    private long nodeStatisticsUpdateFrequency = 10000;
 
     public void setPersistenceService(PersistenceService persistenceService) {
         this.persistenceService = persistenceService;
@@ -88,18 +86,6 @@ public class ClusterServiceImpl implements ClusterService {
         this.osgiConfigurationAdmin = osgiConfigurationAdmin;
     }
 
-    public void setKarafJMXUsername(String karafJMXUsername) {
-        this.karafJMXUsername = karafJMXUsername;
-    }
-
-    public void setKarafJMXPassword(String karafJMXPassword) {
-        this.karafJMXPassword = karafJMXPassword;
-    }
-
-    public void setKarafJMXPort(int karafJMXPort) {
-        this.karafJMXPort = karafJMXPort;
-    }
-
     public void setPublicAddress(String publicAddress) {
         this.publicAddress = publicAddress;
     }
@@ -108,11 +94,19 @@ public class ClusterServiceImpl implements ClusterService {
         this.internalAddress = internalAddress;
     }
 
+    public void setNodeStatisticsUpdateFrequency(long nodeStatisticsUpdateFrequency) {
+        this.nodeStatisticsUpdateFrequency = nodeStatisticsUpdateFrequency;
+    }
+
+    public Map<String, Map<String, Serializable>> getNodeSystemStatistics() {
+        return nodeSystemStatistics;
+    }
+
     public void init() {
         if (karafCellarEventProducer != null && karafCellarClusterManager != null) {
 
             boolean setupConfigOk = true;
-            Group group = karafCellarGroupManager.findGroupByName(karafCellarGroupName);
+            group = karafCellarGroupManager.findGroupByName(karafCellarGroupName);
             if (setupConfigOk && group == null) {
                 logger.error("Cluster group " + karafCellarGroupName + " doesn't exist, creating it...");
                 group = karafCellarGroupManager.createGroup(karafCellarGroupName);
@@ -155,20 +149,21 @@ public class ClusterServiceImpl implements ClusterService {
                 clusterConfigurationEvent.setSourceGroup(group);
                 karafCellarEventProducer.produce(clusterConfigurationEvent);
             }
+
+            nodeStatisticsUpdateTimer = new Timer();
+            TimerTask statisticsTask = new TimerTask() {
+                @Override
+                public void run() {
+                    updateSystemStats();
+                }
+            };
+            nodeStatisticsUpdateTimer.schedule(statisticsTask, 0, nodeStatisticsUpdateFrequency);
+
         }
         logger.info("Cluster service initialized.");
     }
 
     public void destroy() {
-        for (Map.Entry<String, JMXConnector> jmxConnectorEntry : jmxConnectors.entrySet()) {
-            String url = jmxConnectorEntry.getKey();
-            JMXConnector jmxConnector = jmxConnectorEntry.getValue();
-            try {
-                jmxConnector.close();
-            } catch (IOException e) {
-                logger.error("Error closing JMX connector for url {}", url, e);
-            }
-        }
         logger.info("Cluster service shutdown.");
     }
 
@@ -196,43 +191,22 @@ public class ClusterServiceImpl implements ClusterService {
             if (internalEndpoint != null) {
                 clusterNode.setInternalHostAddress(internalEndpoint);
             }
-            String serviceUrl = "service:jmx:rmi:///jndi/rmi://" + karafCellarNode.getHost() + ":" + karafJMXPort + "/karaf-root";
-            try {
-                JMXConnector jmxConnector = getJMXConnector(serviceUrl);
-                MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();
-                final RuntimeMXBean remoteRuntime = ManagementFactory.newPlatformMXBeanProxy(mbsc, ManagementFactory.RUNTIME_MXBEAN_NAME, RuntimeMXBean.class);
-                clusterNode.setUptime(remoteRuntime.getUptime());
-                ObjectName operatingSystemMXBeanName = new ObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME);
-                Double systemCpuLoad = null;
-                try {
-                    systemCpuLoad = (Double) mbsc.getAttribute(operatingSystemMXBeanName, "SystemCpuLoad");
-                } catch (MBeanException e) {
-                    logger.error("Error retrieving system CPU load", e);
-                } catch (AttributeNotFoundException e) {
-                    logger.error("Error retrieving system CPU load", e);
+            Map<String,Serializable> nodeStatistics = nodeSystemStatistics.get(karafCellarNode.getId());
+            if (nodeStatistics != null) {
+                Long uptime = (Long) nodeStatistics.get("uptime");
+                if (uptime != null) {
+                    clusterNode.setUptime(uptime);
                 }
-                final OperatingSystemMXBean remoteOperatingSystemMXBean = ManagementFactory.newPlatformMXBeanProxy(mbsc, ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, OperatingSystemMXBean.class);
-                clusterNode.setLoadAverage(new double[]{remoteOperatingSystemMXBean.getSystemLoadAverage()});
+                Double systemCpuLoad = (Double) nodeStatistics.get("systemCpuLoad");
                 if (systemCpuLoad != null) {
                     clusterNode.setCpuLoad(systemCpuLoad);
                 }
-
-            } catch (MalformedURLException e) {
-                logger.error("Error connecting to remote JMX server", e);
-            } catch (ConnectException ce) {
-                handleTimeouts(serviceUrl, ce);
-            } catch (java.rmi.ConnectException ce) {
-                handleTimeouts(serviceUrl, ce);
-            } catch (java.rmi.ConnectIOException cioe) {
-                handleTimeouts(serviceUrl, cioe);
-            } catch (IOException e) {
-                logger.error("Error retrieving remote JMX data", e);
-            } catch (MalformedObjectNameException e) {
-                logger.error("Error retrieving remote JMX data", e);
-            } catch (InstanceNotFoundException e) {
-                logger.error("Error retrieving remote JMX data", e);
-            } catch (ReflectionException e) {
-                logger.error("Error retrieving remote JMX data", e);
+                List<Double> loadAverage = (List<Double>) nodeStatistics.get("systemLoadAverage");
+                if (loadAverage != null) {
+                    Double[] loadAverageArray = loadAverage.toArray(new Double[loadAverage.size()]);
+                    ArrayUtils.toPrimitive(loadAverageArray);
+                    clusterNode.setLoadAverage(ArrayUtils.toPrimitive(loadAverageArray));
+                }
             }
             clusterNodes.put(karafCellarNode.getId(), clusterNode);
         }
@@ -240,23 +214,6 @@ public class ClusterServiceImpl implements ClusterService {
         return new ArrayList<ClusterNode>(clusterNodes.values());
     }
 
-    private void handleTimeouts(String serviceUrl, Throwable throwable) {
-        Throwable rootCause = throwable;
-        while (rootCause.getCause() != null) {
-            rootCause = rootCause.getCause();
-        }
-        logger.warn("JMX RMI Connection error, will reconnect on next request. Active debug logging for access to detailed stack trace. Root cause=" + rootCause.getMessage());
-        logger.debug("Detailed stacktrace", throwable);
-        JMXConnector jmxConnector = jmxConnectors.remove(serviceUrl);
-        try {
-            if (jmxConnector != null) {
-                jmxConnector.close();
-            }
-        } catch (Throwable t) {
-            // ignore any exception when closing a timed out connection.
-        }
-    }
-
     @Override
     public void purge(Date date) {
         persistenceService.purge(date);
@@ -284,39 +241,6 @@ public class ClusterServiceImpl implements ClusterService {
         return support.isAllowed(group, category, pid, type);
     }
 
-    private JMXConnector getJMXConnector(String url) throws IOException {
-        if (jmxConnectors.containsKey(url)) {
-            JMXConnector jmxConnector = jmxConnectors.get(url);
-            try {
-                jmxConnector.getMBeanServerConnection();
-                return jmxConnector;
-            } catch (IOException e) {
-                jmxConnectors.remove(url);
-                try {
-                    jmxConnector.close();
-                } catch (IOException e1) {
-                    logger.warn("Closing invalid JMX connection resulted in :" + e1.getMessage() + ", this is probably ok.");
-                    logger.debug("Error closing invalid JMX connection", e1);
-                }
-                if (e.getMessage() != null && e.getMessage().contains("Connection closed")) {
-                    logger.warn("JMX connection to url {} was closed (Cause:{}). Reconnecting...", url, e.getMessage());
-                } else {
-                    logger.error("Error using the JMX connection to url {}, closed and will reconnect", url, e);
-                }
-            }
-        }
-        // if we reach this point either we didn't have a connector or it didn't validate
-        // now let's connect to remote JMX service to retrieve information from the runtime and operating system MX beans
-        JMXServiceURL jmxServiceURL = new JMXServiceURL(url);
-        Map<String, Object> environment = new HashMap<String, Object>();
-        if (karafJMXUsername != null && karafJMXPassword != null) {
-            environment.put(JMXConnector.CREDENTIALS, new String[]{karafJMXUsername, karafJMXPassword});
-        }
-        JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, environment);
-        jmxConnectors.put(url, jmxConnector);
-        return jmxConnector;
-    }
-
     private Map<String, String> getMapProperty(Properties properties, String propertyName, String defaultValue) {
         String propertyValue = properties.getProperty(propertyName, defaultValue);
         return getMapProperty(propertyValue);
@@ -350,4 +274,37 @@ public class ClusterServiceImpl implements ClusterService {
         return getMapProperty(oldPropertyValue);
     }
 
+    private void updateSystemStats() {
+        final RuntimeMXBean remoteRuntime = ManagementFactory.getRuntimeMXBean();
+        long uptime = remoteRuntime.getUptime();
+        ObjectName operatingSystemMXBeanName = ManagementFactory.getOperatingSystemMXBean().getObjectName();
+        Double systemCpuLoad = null;
+        try {
+            systemCpuLoad = (Double) ManagementFactory.getPlatformMBeanServer().getAttribute(operatingSystemMXBeanName, "SystemCpuLoad");
+        } catch (MBeanException e) {
+            logger.error("Error retrieving system CPU load", e);
+        } catch (AttributeNotFoundException e) {
+            logger.error("Error retrieving system CPU load", e);
+        } catch (InstanceNotFoundException e) {
+            logger.error("Error retrieving system CPU load", e);
+        } catch (ReflectionException e) {
+            logger.error("Error retrieving system CPU load", e);
+        }
+        final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
+        double systemLoadAverage = operatingSystemMXBean.getSystemLoadAverage();
+
+        ClusterSystemStatisticsEvent clusterSystemStatisticsEvent = new ClusterSystemStatisticsEvent("org.apache.unomi.cluster.system.statistics");
+        clusterSystemStatisticsEvent.setSourceGroup(group);
+        clusterSystemStatisticsEvent.setSourceNode(karafCellarClusterManager.getNode());
+        Map<String,Serializable> systemStatistics = new TreeMap<>();
+        ArrayList<Double> systemLoadAverageArray = new ArrayList<>();
+        systemLoadAverageArray.add(systemLoadAverage);
+        systemStatistics.put("systemLoadAverage", systemLoadAverageArray);
+        systemStatistics.put("systemCpuLoad", systemCpuLoad);
+        systemStatistics.put("uptime", uptime);
+        clusterSystemStatisticsEvent.setStatistics(systemStatistics);
+        nodeSystemStatistics.put(karafCellarClusterManager.getNode().getId(), systemStatistics);
+        karafCellarEventProducer.produce(clusterSystemStatisticsEvent);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/7b19dfa0/services/src/main/java/org/apache/unomi/services/services/ClusterSystemStatisticsEvent.java
----------------------------------------------------------------------
diff --git a/services/src/main/java/org/apache/unomi/services/services/ClusterSystemStatisticsEvent.java b/services/src/main/java/org/apache/unomi/services/services/ClusterSystemStatisticsEvent.java
new file mode 100644
index 0000000..cbe162d
--- /dev/null
+++ b/services/src/main/java/org/apache/unomi/services/services/ClusterSystemStatisticsEvent.java
@@ -0,0 +1,43 @@
+/*
+ * 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.unomi.services.services;
+
+import org.apache.karaf.cellar.core.event.Event;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * The cluster event used to transmit update to node system statistics.
+ */
+public class ClusterSystemStatisticsEvent extends Event {
+
+    Map<String,Serializable> statistics = new TreeMap<>();
+
+    public ClusterSystemStatisticsEvent(String id) {
+        super(id);
+    }
+
+    public Map<String, Serializable> getStatistics() {
+        return statistics;
+    }
+
+    public void setStatistics(Map<String, Serializable> statistics) {
+        this.statistics = statistics;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/7b19dfa0/services/src/main/java/org/apache/unomi/services/services/ClusterSystemStatisticsEventHandler.java
----------------------------------------------------------------------
diff --git a/services/src/main/java/org/apache/unomi/services/services/ClusterSystemStatisticsEventHandler.java b/services/src/main/java/org/apache/unomi/services/services/ClusterSystemStatisticsEventHandler.java
new file mode 100644
index 0000000..2fef85c
--- /dev/null
+++ b/services/src/main/java/org/apache/unomi/services/services/ClusterSystemStatisticsEventHandler.java
@@ -0,0 +1,138 @@
+/*
+ * 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.unomi.services.services;
+
+import org.apache.karaf.cellar.config.Constants;
+import org.apache.karaf.cellar.core.CellarSupport;
+import org.apache.karaf.cellar.core.Configurations;
+import org.apache.karaf.cellar.core.Group;
+import org.apache.karaf.cellar.core.control.BasicSwitch;
+import org.apache.karaf.cellar.core.control.Switch;
+import org.apache.karaf.cellar.core.control.SwitchStatus;
+import org.apache.karaf.cellar.core.event.EventHandler;
+import org.apache.karaf.cellar.core.event.EventType;
+import org.osgi.service.cm.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A Karaf Cellar event handler to process incoming events that contain system statistics updates from nodes.
+ */
+public class ClusterSystemStatisticsEventHandler extends CellarSupport implements EventHandler<ClusterSystemStatisticsEvent> {
+
+    public static final String SWITCH_ID = "org.apache.unomi.cluster.system.statistics.handler";
+    private static final Logger logger = LoggerFactory.getLogger(ClusterSystemStatisticsEventHandler.class.getName());
+    private final Switch eventSwitch = new BasicSwitch(SWITCH_ID);
+    private ClusterServiceImpl clusterServiceImpl;
+
+    public void setClusterServiceImpl(ClusterServiceImpl clusterServiceImpl) {
+        this.clusterServiceImpl = clusterServiceImpl;
+    }
+
+    public void init() {
+        // nothing to do
+    }
+
+    public void destroy() {
+        // nothing to do
+    }
+
+    @Override
+    public void handle(ClusterSystemStatisticsEvent event) {
+        // check if the handler is ON
+        if (this.getSwitch().getStatus().equals(SwitchStatus.OFF)) {
+            logger.debug("CELLAR SYSTEM STATS: {} switch is OFF, cluster event not handled", SWITCH_ID);
+            return;
+        }
+
+        if (groupManager == null) {
+            //in rare cases for example right after installation this happens!
+            logger.error("CELLAR SYSTEM STATS: retrieved event {} while groupManager is not available yet!", event);
+            return;
+        }
+
+        // check if the group is local
+        if (!groupManager.isLocalGroup(event.getSourceGroup().getName())) {
+            logger.debug("CELLAR SYSTEM STATS: node is not part of the event cluster group {}",event.getSourceGroup().getName());
+            return;
+        }
+
+        Group group = event.getSourceGroup();
+        String groupName = group.getName();
+
+        String pid = event.getId();
+
+        if (isAllowed(event.getSourceGroup(), Constants.CATEGORY, pid, EventType.INBOUND)) {
+
+            // check if it's not a "local" event
+            if (event.getSourceNode() != null && event.getSourceNode().getId().equalsIgnoreCase(clusterManager.getNode().getId())) {
+                logger.trace("CELLAR SYSTEM STATS: cluster event is local (coming from local synchronizer or listener)");
+                return;
+            }
+
+
+            Map<String, Serializable> nodeSystemStatistics = clusterServiceImpl.getNodeSystemStatistics().get(event.getSourceNode().getId());
+            if (nodeSystemStatistics == null) {
+                nodeSystemStatistics = new ConcurrentHashMap<>();
+            }
+            nodeSystemStatistics.putAll(event.getStatistics());
+            clusterServiceImpl.getNodeSystemStatistics().put(event.getSourceNode().getId(), nodeSystemStatistics);
+        }
+
+    }
+
+    @Override
+    public Class<ClusterSystemStatisticsEvent> getType() {
+        return ClusterSystemStatisticsEvent.class;
+    }
+
+    /**
+     * Get the cluster configuration event handler switch.
+     *
+     * @return the cluster configuration event handler switch.
+     */
+    @Override
+    public Switch getSwitch() {
+        // load the switch status from the config
+        try {
+            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
+            if (configuration != null) {
+                String handlerStatus = (String) configuration.getProperties().get(Configurations.HANDLER + "." + this.getClass().getName());
+                if (handlerStatus == null) {
+                    // default value is on.
+                    eventSwitch.turnOn();
+                } else {
+                    Boolean status = new Boolean(handlerStatus);
+                    if (status) {
+                        eventSwitch.turnOn();
+                    } else {
+                        eventSwitch.turnOff();
+                    }
+                }
+            }
+        } catch (Exception e) {
+            // nothing to do
+        }
+        return eventSwitch;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/7b19dfa0/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
----------------------------------------------------------------------
diff --git a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index c18bedd..3d9b181 100644
--- a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -37,11 +37,9 @@
                              update-strategy="reload" placeholder-prefix="${cluster.">
         <cm:default-properties>
             <cm:property name="group" value="default" />
-            <cm:property name="jmxUsername" value="karaf" />
-            <cm:property name="jmxPassword" value="karaf" />
-            <cm:property name="jmxPort" value="1099" />
             <cm:property name="contextserver.publicAddress" value="https://localhost:9443"/>
             <cm:property name="contextserver.internalAddress" value="http://127.0.0.1:8181"/>
+            <cm:property name="nodeStatisticsUpdateFrequency" value="10000"/>
         </cm:default-properties>
     </cm:property-placeholder>
 
@@ -165,9 +163,7 @@
         <property name="karafCellarGroupManager" ref="karafCellarGroupManager" />
         <property name="karafCellarGroupName" value="${cluster.group}" />
         <property name="osgiConfigurationAdmin" ref="osgiConfigurationAdmin" />
-        <property name="karafJMXUsername" value="${cluster.jmxUsername}" />
-        <property name="karafJMXPassword" value="${cluster.jmxPassword}" />
-        <property name="karafJMXPort" value="${cluster.jmxPort}" />
+        <property name="nodeStatisticsUpdateFrequency" value="${cluster.nodeStatisticsUpdateFrequency}" />
     </bean>
     <service id="clusterService" ref="clusterServiceImpl" auto-export="interfaces"/>
 
@@ -282,4 +278,19 @@
     <service id="configSharingService" ref="configSharingServiceImpl" auto-export="interfaces">
     </service>
 
+    <!-- Cluster System Statistics Event Handler -->
+    <bean id="clusterSystemStatisticsEventHandler" class="org.apache.unomi.services.services.ClusterSystemStatisticsEventHandler"
+          init-method="init" destroy-method="destroy">
+        <property name="configurationAdmin" ref="osgiConfigurationAdmin"/>
+        <property name="clusterManager" ref="karafCellarClusterManager"/>
+        <property name="groupManager" ref="karafCellarGroupManager"/>
+        <property name="clusterServiceImpl" ref="clusterServiceImpl" />
+    </bean>
+    <service ref="clusterSystemStatisticsEventHandler" interface="org.apache.karaf.cellar.core.event.EventHandler">
+        <service-properties>
+            <entry key="managed" value="true"/>
+        </service-properties>
+    </service>
+
+
 </blueprint>

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/7b19dfa0/services/src/main/resources/org.apache.unomi.cluster.cfg
----------------------------------------------------------------------
diff --git a/services/src/main/resources/org.apache.unomi.cluster.cfg b/services/src/main/resources/org.apache.unomi.cluster.cfg
index 9bdc7af..b6498e7 100644
--- a/services/src/main/resources/org.apache.unomi.cluster.cfg
+++ b/services/src/main/resources/org.apache.unomi.cluster.cfg
@@ -15,8 +15,11 @@
 # limitations under the License.
 #
 group=default
-jmxUsername=karaf
-jmxPassword=karaf
-jmxPort=1099
 contextserver.publicAddress=https://localhost:9443
 contextserver.internalAddress=http://127.0.0.1:8181
+#
+# The nodeStatisticsUpdateFrequency controls the frequency of the update of system statistics such as CPU load,
+# system load average and uptime. This value is set in milliseconds and is set to 10 seconds by default. Each node
+# will retrieve the local values and broadcast them through a cluster event to all the other nodes to update
+# the global cluster statistics.
+nodeStatisticsUpdateFrequency=10000
\ No newline at end of file