You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by ss...@apache.org on 2013/11/08 17:37:30 UTC

git commit: first version of Zookeeper integration (MARMOTTA-354)

Updated Branches:
  refs/heads/develop 1d40d1333 -> eea9f0944


first version of Zookeeper integration (MARMOTTA-354)


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

Branch: refs/heads/develop
Commit: eea9f0944d2b6fff38424022ebdf318f0869355e
Parents: 1d40d13
Author: Sebastian Schaffert <ss...@apache.org>
Authored: Fri Nov 8 17:37:24 2013 +0100
Committer: Sebastian Schaffert <ss...@apache.org>
Committed: Fri Nov 8 17:37:24 2013 +0100

----------------------------------------------------------------------
 launchers/marmotta-webapp/pom.xml               |   5 +
 .../backend/kiwi/KiWiStoreProvider.java         |  23 ++-
 .../main/resources/config-defaults.properties   |   4 -
 .../src/main/resources/kiwi-module.properties   |   3 +
 .../main/resources/web/admin/configuration.html |  42 ++--
 .../src/main/resources/web/admin/console.html   |  37 ++++
 .../src/main/resources/web/admin/database.html  |  63 +++---
 .../resources/web/admin/js/widgets/database.js  |  21 +-
 .../core/api/config/ConfigurationService.java   |  13 +-
 .../config/ConfigurationServiceImpl.java        |  36 +++-
 platform/marmotta-zookeeper/pom.xml             | 159 +++++++++++++++
 .../zookeeper/api/ZookeeperService.java         |  33 ++++
 .../zookeeper/event/ZookeeperInitEvent.java     |  28 +++
 .../exception/ConfigurationException.java       | 100 ++++++++++
 .../listeners/ConfigurationListener.java        | 177 +++++++++++++++++
 .../services/ZookeeperServiceImpl.java          | 193 +++++++++++++++++++
 .../src/main/resources/META-INF/beans.xml       |  28 +++
 .../src/main/resources/kiwi-module.properties   |  38 ++++
 platform/pom.xml                                |   2 +
 19 files changed, 938 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/launchers/marmotta-webapp/pom.xml
----------------------------------------------------------------------
diff --git a/launchers/marmotta-webapp/pom.xml b/launchers/marmotta-webapp/pom.xml
index c048c35..4c5a0fe 100644
--- a/launchers/marmotta-webapp/pom.xml
+++ b/launchers/marmotta-webapp/pom.xml
@@ -221,6 +221,11 @@
             <artifactId>marmotta-versioning-kiwi</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.marmotta</groupId>
+            <artifactId>marmotta-zookeeper</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <!--
         <dependency>
             <groupId>org.apache.marmotta</groupId>

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/backends/marmotta-backend-kiwi/src/main/java/org/apache/marmotta/platform/backend/kiwi/KiWiStoreProvider.java
----------------------------------------------------------------------
diff --git a/platform/backends/marmotta-backend-kiwi/src/main/java/org/apache/marmotta/platform/backend/kiwi/KiWiStoreProvider.java b/platform/backends/marmotta-backend-kiwi/src/main/java/org/apache/marmotta/platform/backend/kiwi/KiWiStoreProvider.java
index 3419578..578ba78 100644
--- a/platform/backends/marmotta-backend-kiwi/src/main/java/org/apache/marmotta/platform/backend/kiwi/KiWiStoreProvider.java
+++ b/platform/backends/marmotta-backend-kiwi/src/main/java/org/apache/marmotta/platform/backend/kiwi/KiWiStoreProvider.java
@@ -58,7 +58,11 @@ import javax.inject.Inject;
 @ApplicationScoped
 public class KiWiStoreProvider implements StoreProvider {
 
-    public static final String SPARQL_STRATEGY = "sparql.strategy";
+    public static final String SPARQL_STRATEGY    = "sparql.strategy";
+    public static final String DATACENTER_ID      = "database.datacenter.id";
+    public static final String FULLTEXT_ENABLED   = "database.fulltext.enabled";
+    public static final String FULLTEXT_LANGUAGES = "database.fulltext.languages";
+    public static final String DEBUG_SLOWQUERIES = "database.debug.slowqueries";
 
     @Inject
     private Logger log;
@@ -94,13 +98,13 @@ public class KiWiStoreProvider implements StoreProvider {
         String dbPass  = configurationService.getStringConfiguration("database.password");
 
         KiWiConfiguration configuration = new KiWiConfiguration("lmf", jdbcUrl, dbUser, dbPass, dialect, configurationService.getDefaultContext(), configurationService.getInferredContext());
-        configuration.setQueryLoggingEnabled(configurationService.getBooleanConfiguration("database.debug.slowqueries", false));
+        configuration.setQueryLoggingEnabled(configurationService.getBooleanConfiguration(DEBUG_SLOWQUERIES, false));
         configuration.setTripleBatchCommit(configurationService.getBooleanConfiguration("database.triples.batchcommit", true));
         configuration.setTripleBatchSize(configurationService.getIntConfiguration("database.triples.batchsize", 10000));
 
-        configuration.setDatacenterId(configurationService.getIntConfiguration("database.datacenter.id",0));
-        configuration.setFulltextEnabled(configurationService.getBooleanConfiguration("database.fulltext.enabled", true));
-        configuration.setFulltextLanguages(configurationService.getListConfiguration("database.fulltext.languages", ImmutableList.of("en")));
+        configuration.setDatacenterId(configurationService.getIntConfiguration(DATACENTER_ID,0));
+        configuration.setFulltextEnabled(configurationService.getBooleanConfiguration(FULLTEXT_ENABLED, true));
+        configuration.setFulltextLanguages(configurationService.getListConfiguration(FULLTEXT_LANGUAGES, ImmutableList.of("en")));
 
         if("native".equalsIgnoreCase(configurationService.getStringConfiguration(SPARQL_STRATEGY))) {
             return new KiWiSparqlSail(new KiWiStore(configuration));
@@ -142,7 +146,14 @@ public class KiWiStoreProvider implements StoreProvider {
     }
 
     public void configurationChanged(@Observes ConfigurationChangedEvent e) {
-        if(e.containsChangedKey(SPARQL_STRATEGY)) {
+        if(e.containsChangedKey(SPARQL_STRATEGY) ||
+                e.containsChangedKey(DATACENTER_ID) ||
+                e.containsChangedKey(FULLTEXT_ENABLED) ||
+                e.containsChangedKey(FULLTEXT_LANGUAGES) ||
+                e.containsChangedKey(DEBUG_SLOWQUERIES)
+                ) {
+            log.info("KiWi backend configuration changed, re-initialising triple store");
+
             sesameService.restart();
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/backends/marmotta-backend-kiwi/src/main/resources/config-defaults.properties
----------------------------------------------------------------------
diff --git a/platform/backends/marmotta-backend-kiwi/src/main/resources/config-defaults.properties b/platform/backends/marmotta-backend-kiwi/src/main/resources/config-defaults.properties
index c39777f..9ff7f78 100644
--- a/platform/backends/marmotta-backend-kiwi/src/main/resources/config-defaults.properties
+++ b/platform/backends/marmotta-backend-kiwi/src/main/resources/config-defaults.properties
@@ -68,7 +68,3 @@ database.postgres.url = jdbc:postgresql://localhost:5432/lmf?prepareThreshold=3
 database.mysql.driver = com.mysql.jdbc.Driver
 database.mysql.url = jdbc:mysql://localhost:3306/lmf?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true
 
-# Hibernate-specific configuration for Oracle
-database.oracle.url = jdbc:oracle:thin:@//localhost:1521/XE
-database.oracle.driver = oracle.jdbc.OracleDriver
-

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/backends/marmotta-backend-kiwi/src/main/resources/kiwi-module.properties
----------------------------------------------------------------------
diff --git a/platform/backends/marmotta-backend-kiwi/src/main/resources/kiwi-module.properties b/platform/backends/marmotta-backend-kiwi/src/main/resources/kiwi-module.properties
index de7f308..0454b6c 100644
--- a/platform/backends/marmotta-backend-kiwi/src/main/resources/kiwi-module.properties
+++ b/platform/backends/marmotta-backend-kiwi/src/main/resources/kiwi-module.properties
@@ -37,5 +37,8 @@ adminpage.1.link=/admin/configuration.html
 adminpage.2.title=Database
 adminpage.2.link=/admin/database.html
 
+adminpage.3.title=Console
+adminpage.3.link=/admin/console.html
+
 webservices=org.apache.marmotta.platform.backend.kiwi.DatabaseWebService
   
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/configuration.html
----------------------------------------------------------------------
diff --git a/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/configuration.html b/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/configuration.html
index afa5287..3957686 100644
--- a/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/configuration.html
+++ b/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/configuration.html
@@ -16,31 +16,37 @@
   -->
 <html>
 <head>
+<title>Storage Backend: BigData</title>
+<script type="text/javascript">
+	var _SERVER_URL = "http://localhost:8080/LMF/";
+</script>
 <!--###BEGIN_HEAD###-->
-    <title>Core Module Configuration</title>
-    <link type="text/css" rel="stylesheet" href="../../../../../../../marmotta-core/src/main/resources/web/public/js/widgets/configurator/style.css">
     <script type="text/javascript" src="../../webjars/jquery/1.8.2/jquery.min.js"></script>
-    <script type="text/javascript" src="js/widgets/database.js"></script>
+    <script type="text/javascript" src="../../core/public/js/widgets/configurator/configurator.js"></script>
+    <link rel="stylesheet" href="style.css" />
+    <link type="text/css" rel="stylesheet" href="../../core/public/js/widgets/configurator/style.css">
     <script type="text/javascript">
         jQuery(document).ready(function(){
-            $('#lmf_database').lmf_database({host:_SERVER_URL});
+            var options = {
+                url : _SERVER_URL,
+                container : "kiwi_configurator",
+                prefix : 'database',
+                blacklist: ['database.type','database.url','database.user','database.password','database.h2.','database.postgres.','database.mysql.']
+            }
+            var configurator = new Configurator(options);
         });
     </script>
 <!--###END_HEAD###-->
 </head>
 <body>
-<div id="main">
-<div id="contents">
-<!--###BEGIN_CONTENT###-->
-<h1>KiWi Storage Backend Configuration</h1>
-        <p>
-            Here you can configure the database options for the Marmotta KiWi Storage Backend.<br/>
-        </p>
-        <div id="lmf_database">
-            <h4>Loading database manager</h4>
-        </div>
-<!--###END_CONTENT###-->
-</div>
-</div>
+  <!--###BEGIN_CONTENT###-->
+  <h1>KiWi Backend Configuration</h1>
+  <p>
+    Here you can configure options offered by the KiWi triple store. The configuration currently contains:
+  </p>
+  <div id="kiwi_configurator">
+    <h4>Loading configurator</h4>
+  </div>
+  <!--###END_CONTENT###-->
 </body>
-</html>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/console.html
----------------------------------------------------------------------
diff --git a/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/console.html b/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/console.html
new file mode 100644
index 0000000..87b6835
--- /dev/null
+++ b/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/console.html
@@ -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.
+
+-->
+<html>
+<head>
+    <!--###BEGIN_HEAD###-->
+    <title>Core Module</title>
+    <script type="text/javascript">
+        (function() {
+            window.open('../../database/');
+        })();
+    </script>
+    <!--###END_HEAD###-->
+</head>
+<body>
+<!--###BEGIN_CONTENT###-->
+<h1>Manage Marmotta database via web-interface</h1>
+<a href="../../database/" target="_blank">Open database administration page in new window.</a>
+<!--###END_CONTENT###-->
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/database.html
----------------------------------------------------------------------
diff --git a/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/database.html b/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/database.html
index 87b6835..afa5287 100644
--- a/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/database.html
+++ b/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/database.html
@@ -1,37 +1,46 @@
 <!--
-
-    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.
-
--->
+  ~ 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.
+  -->
 <html>
 <head>
-    <!--###BEGIN_HEAD###-->
-    <title>Core Module</title>
+<!--###BEGIN_HEAD###-->
+    <title>Core Module Configuration</title>
+    <link type="text/css" rel="stylesheet" href="../../../../../../../marmotta-core/src/main/resources/web/public/js/widgets/configurator/style.css">
+    <script type="text/javascript" src="../../webjars/jquery/1.8.2/jquery.min.js"></script>
+    <script type="text/javascript" src="js/widgets/database.js"></script>
     <script type="text/javascript">
-        (function() {
-            window.open('../../database/');
-        })();
+        jQuery(document).ready(function(){
+            $('#lmf_database').lmf_database({host:_SERVER_URL});
+        });
     </script>
-    <!--###END_HEAD###-->
+<!--###END_HEAD###-->
 </head>
 <body>
+<div id="main">
+<div id="contents">
 <!--###BEGIN_CONTENT###-->
-<h1>Manage Marmotta database via web-interface</h1>
-<a href="../../database/" target="_blank">Open database administration page in new window.</a>
+<h1>KiWi Storage Backend Configuration</h1>
+        <p>
+            Here you can configure the database options for the Marmotta KiWi Storage Backend.<br/>
+        </p>
+        <div id="lmf_database">
+            <h4>Loading database manager</h4>
+        </div>
 <!--###END_CONTENT###-->
+</div>
+</div>
 </body>
-</html>
\ No newline at end of file
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/js/widgets/database.js
----------------------------------------------------------------------
diff --git a/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/js/widgets/database.js b/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/js/widgets/database.js
index 42efb8e..6a0029c 100644
--- a/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/js/widgets/database.js
+++ b/platform/backends/marmotta-backend-kiwi/src/main/resources/web/admin/js/widgets/database.js
@@ -15,6 +15,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+function endsWith(str, suffix) {
+    return str.indexOf(suffix, str.length - suffix.length) !== -1;
+}
+
 (function( $ ){
     $.fn.lmf_database = function(options) {
         var settings = {
@@ -103,13 +108,15 @@
                         var values = [];
                          var databases = new Array();
                          for(var property in data) {
-                            property = property.substring(property.indexOf('.')+1);
-                            property = property.substring(0,property.indexOf('.'));
-                            if(property!="") {
-                                if($.inArray(property,databases)==-1) {
-                                    databases.push(property);
-                                }
-                            }
+                             if(endsWith(property,"url")) {
+                                 property = property.substring(property.indexOf('.')+1);
+                                 property = property.substring(0,property.indexOf('.'));
+                                 if(property!="") {
+                                     if($.inArray(property,databases)==-1) {
+                                         databases.push(property);
+                                     }
+                                 }
+                             }
                         }
                         getDBType(databases,new Array());
                     });

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java
index 58fdab7..6a00235 100644
--- a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java
@@ -20,7 +20,6 @@ package org.apache.marmotta.platform.core.api.config;
 import org.apache.commons.configuration.Configuration;
 
 import javax.servlet.ServletContext;
-
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
@@ -539,4 +538,16 @@ public interface ConfigurationService {
     boolean isInitialising();
 
     void setInitialising(boolean initialising);
+
+
+    /**
+     * Return the context parameter, servlet init parameter, or system property with the given key. This method
+     * provides unified access to properties configured in either the web.xml, the context.xml or via a system property
+     * passed on startup. It can be used by services that cannot read their configuration from the main
+     * system-config.properties.
+     *
+     * @param key
+     * @return
+     */
+    String getContextParam(String key);
 }

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java
index c42a019..6b9e567 100644
--- a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java
@@ -64,7 +64,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 @ApplicationScoped
 public class ConfigurationServiceImpl implements ConfigurationService {
 
-	private String home;
+    private String home;
 
     private static Logger log = LoggerFactory.getLogger(ConfigurationService.class);
 
@@ -176,13 +176,13 @@ public class ConfigurationServiceImpl implements ConfigurationService {
                 if(!f3.exists()) {
                     f3.mkdirs();
                 }
-                
+
                 // ensure directory for importing data
                 File f4 = new File(getHome() + File.separator + DIR_IMPORT);
                 if(!f4.exists()) {
                     f4.mkdirs();
                 }
-                
+
             }
 
             // the save configuration will be in the  home directory
@@ -299,7 +299,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
             log.info("Apache Marmotta Configuration Service: initialisation completed");
 
             configurationInitEvent.fire(new ConfigurationServiceInitEvent());
-            
+
         } finally {
             lock.writeLock().unlock();
         }
@@ -991,6 +991,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
 
 
             if (!initialising) {
+                log.debug("firing configuration changed event for key {}", key);
                 configurationEvent.fire(new ConfigurationChangedEvent(key));
             }
         }
@@ -1018,6 +1019,8 @@ public class ConfigurationServiceImpl implements ConfigurationService {
         Preconditions.checkNotNull(key);
         Preconditions.checkState(initialised,"ConfigurationService not yet initialised; call initialise() manually");
 
+        log.debug("setting configuration {} = {}", key, value);
+
         if(!config.containsKey(key) || !ObjectUtils.equals(value,config.getString(key))) {
 
             lock.writeLock().lock();
@@ -1036,6 +1039,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
 
 
             if (!initialising) {
+                log.debug("firing configuration changed event for key {}", key);
                 configurationEvent.fire(new ConfigurationChangedEvent(key));
             }
         }
@@ -1452,4 +1456,28 @@ public class ConfigurationServiceImpl implements ConfigurationService {
         }
     }
 
+    /**
+     * Return the context parameter, servlet init parameter, or system property with the given key. This method
+     * provides unified access to properties configured in either the web.xml, the context.xml or via a system property
+     * passed on startup. It can be used by services that cannot read their configuration from the main
+     * system-config.properties.
+     *
+     * @param key
+     * @return
+     */
+    @Override
+    public String getContextParam(String key) {
+        String value = System.getProperty(key);
+        if(value == null) {
+            if(servletContext != null) {
+                value = servletContext.getInitParameter(key);
+
+                if(value == null) {
+                    value = servletContext.getAttribute(key) != null ? servletContext.getAttribute(key).toString() : null;
+                }
+
+            }
+        }
+        return value;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-zookeeper/pom.xml
----------------------------------------------------------------------
diff --git a/platform/marmotta-zookeeper/pom.xml b/platform/marmotta-zookeeper/pom.xml
new file mode 100644
index 0000000..0f4152e
--- /dev/null
+++ b/platform/marmotta-zookeeper/pom.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.marmotta</groupId>
+        <artifactId>marmotta-parent</artifactId>
+        <version>3.2.0-incubating-SNAPSHOT</version>
+        <relativePath>../../parent</relativePath>
+    </parent>
+
+    <artifactId>marmotta-zookeeper</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Apache Marmotta Platform: Zookeeper</name>
+    <description>
+        This module provides integration with Apache Zookeeper for cloud configuration management of clusters of
+        Marmotta installations.
+    </description>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin> <!-- generate JRebel Configuration -->
+                    <groupId>org.zeroturnaround</groupId>
+                    <artifactId>jrebel-maven-plugin</artifactId>
+                    <version>1.1.3</version>
+                    <executions>
+                        <execution>
+                            <id>generate-rebel-xml</id>
+                            <phase>process-resources</phase>
+                            <goals>
+                                <goal>generate</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                    <configuration>
+                        <relativePath>../../</relativePath>
+                        <rootPath>$${rebel.root}</rootPath>
+                        <classpath>
+                            <resources>
+                                <resource><!-- default resource --></resource>
+                                <resource><directory>src/main/resources</directory></resource>
+                            </resources>
+                        </classpath>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.marmotta</groupId>
+                <artifactId>buildinfo-maven-plugin</artifactId>
+                <configuration>
+                    <systemProperties>
+                        <systemProperty>user.name</systemProperty>
+                        <systemProperty>user.timezone</systemProperty>
+                        <systemProperty>java.vm.vendor</systemProperty>
+                        <systemProperty>java.vm.version</systemProperty>
+                        <systemProperty>java.vm.name</systemProperty>
+                        <systemProperty>java.runtime.version</systemProperty>
+                        <systemProperty>os.name</systemProperty>
+                        <systemProperty>os.version</systemProperty>
+                        <systemProperty>os.arch</systemProperty>
+                    </systemProperties>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>extract</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <executions>
+                    <!--
+                    <execution>
+                        <id>aggregate</id>
+                        <goals>
+                            <goal>aggregate</goal>
+                        </goals>
+                        <phase>site</phase>
+                    </execution>
+                    -->
+                    <execution>
+                        <!-- configure how the REST API documentation will be produced -->
+                        <id>restapi</id>
+                        <configuration>
+                            <doclet>com.lunatech.doclets.jax.jaxrs.JAXRSDoclet</doclet>
+
+                            <name>REST API</name>
+                            <description>REST API for LMF Webservices</description>
+
+                            <outputDirectory>${project.build.outputDirectory}/doc</outputDirectory>
+                            <reportOutputDirectory>${project.build.outputDirectory}/web/doc</reportOutputDirectory>
+                            <destDir>rest</destDir>
+
+                            <docletArtifact>
+                                <groupId>com.lunatech.jax-doclets</groupId>
+                                <artifactId>doclets</artifactId>
+                                <version>0.10.0</version>
+                            </docletArtifact>
+                            <additionalparam>
+                                -jaxrscontext {BASE}
+                                -charset UTF-8
+                            </additionalparam>
+
+                            <!--
+                                                        <stylesheetfile>${project.parent.basedir}/config/doc/doclet.css</stylesheetfile>
+                            -->
+
+                            <header><![CDATA[<!--###BEGIN_CONTENT###--><div class="javadoc">]]></header>
+                            <footer><![CDATA[</div><!--###END_CONTENT###-->]]></footer>
+                            <encoding>UTF-8</encoding>
+                            <detectOfflineLinks>false</detectOfflineLinks>
+
+                            <!-- For the project-reports page -->
+                        </configuration>
+                        <goals>
+                            <goal>javadoc</goal>
+                        </goals>
+                        <phase>generate-resources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.marmotta</groupId>
+            <artifactId>marmotta-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>at.salzburgresearch.nodekeeper</groupId>
+            <artifactId>nodekeeper-java</artifactId>
+            <version>1.0</version>
+
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>org.apache.felix.fileinstall</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/api/ZookeeperService.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/api/ZookeeperService.java b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/api/ZookeeperService.java
new file mode 100644
index 0000000..b28b14e
--- /dev/null
+++ b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/api/ZookeeperService.java
@@ -0,0 +1,33 @@
+/*
+ * 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.marmotta.platform.zookeeper.api;
+
+/**
+ * Add file description here!
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public interface ZookeeperService {
+
+    public static final String ZK_SERVER   = "zookeeper.server";
+    public static final String ZK_TIMEOUT  = "zookeeper.timeout";
+    public static final String ZK_INSTANCE = "zookeeper.instance";
+    public static final String ZK_CLUSTER  = "zookeeper.cluster";
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/event/ZookeeperInitEvent.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/event/ZookeeperInitEvent.java b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/event/ZookeeperInitEvent.java
new file mode 100644
index 0000000..cf7c904
--- /dev/null
+++ b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/event/ZookeeperInitEvent.java
@@ -0,0 +1,28 @@
+/*
+ * 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.marmotta.platform.zookeeper.event;
+
+/**
+ * Thrown when Zookeeper connection is initialised.
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class ZookeeperInitEvent {
+    public ZookeeperInitEvent() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/exception/ConfigurationException.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/exception/ConfigurationException.java b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/exception/ConfigurationException.java
new file mode 100644
index 0000000..03ba686
--- /dev/null
+++ b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/exception/ConfigurationException.java
@@ -0,0 +1,100 @@
+/*
+ * 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.marmotta.platform.zookeeper.exception;
+
+/**
+ * Add file description here!
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class ConfigurationException extends Exception {
+
+    /**
+     * Constructs a new exception with {@code null} as its detail message.
+     * The cause is not initialized, and may subsequently be initialized by a
+     * call to {@link #initCause}.
+     */
+    public ConfigurationException() {
+    }
+
+    /**
+     * Constructs a new exception with the specified cause and a detail
+     * message of <tt>(cause==null ? null : cause.toString())</tt> (which
+     * typically contains the class and detail message of <tt>cause</tt>).
+     * This constructor is useful for exceptions that are little more than
+     * wrappers for other throwables (for example, {@link
+     * java.security.PrivilegedActionException}).
+     *
+     * @param cause the cause (which is saved for later retrieval by the
+     *              {@link #getCause()} method).  (A <tt>null</tt> value is
+     *              permitted, and indicates that the cause is nonexistent or
+     *              unknown.)
+     * @since 1.4
+     */
+    public ConfigurationException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message.  The
+     * cause is not initialized, and may subsequently be initialized by
+     * a call to {@link #initCause}.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public ConfigurationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and
+     * cause.  <p>Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this exception's detail message.
+     *
+     * @param message the detail message (which is saved for later retrieval
+     *                by the {@link #getMessage()} method).
+     * @param cause   the cause (which is saved for later retrieval by the
+     *                {@link #getCause()} method).  (A <tt>null</tt> value is
+     *                permitted, and indicates that the cause is nonexistent or
+     *                unknown.)
+     * @since 1.4
+     */
+    public ConfigurationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message,
+     * cause, suppression enabled or disabled, and writable stack
+     * trace enabled or disabled.
+     *
+     * @param message            the detail message.
+     * @param cause              the cause.  (A {@code null} value is permitted,
+     *                           and indicates that the cause is nonexistent or unknown.)
+     * @param enableSuppression  whether or not suppression is enabled
+     *                           or disabled
+     * @param writableStackTrace whether or not the stack trace should
+     *                           be writable
+     * @since 1.7
+     */
+    public ConfigurationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/listeners/ConfigurationListener.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/listeners/ConfigurationListener.java b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/listeners/ConfigurationListener.java
new file mode 100644
index 0000000..ffbf52f
--- /dev/null
+++ b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/listeners/ConfigurationListener.java
@@ -0,0 +1,177 @@
+/*
+ * 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.marmotta.platform.zookeeper.listeners;
+
+import at.salzburgresearch.nodekeeper.NodeKeeper;
+import at.salzburgresearch.nodekeeper.NodeListener;
+import at.salzburgresearch.nodekeeper.exception.NodeKeeperException;
+import at.salzburgresearch.nodekeeper.model.Node;
+import org.apache.marmotta.platform.core.api.config.ConfigurationService;
+import org.apache.marmotta.platform.zookeeper.api.ZookeeperService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.UUID;
+
+/**
+ * Add file description here!
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class ConfigurationListener extends NodeListener<String> {
+
+    private static Logger log = LoggerFactory.getLogger(ConfigurationListener.class);
+
+    private ConfigurationService configurationService;
+
+    public ConfigurationListener(ConfigurationService configurationService, NodeKeeper nodekeeper) {
+        this.configurationService = configurationService;
+        this.nodekeeper            = nodekeeper;
+    }
+
+    @Override
+    public void onNodeCreated(Node<String> node) {
+        ConfigurationLevel level = getConfigurationLevel(node);
+
+        String key = node.getPath().substring(node.getPath().lastIndexOf("/")+1);
+        String value = node.getData();
+
+        try {
+            // look if there is a more specific existing configuration; if yes, issue a message and return
+            for(ConfigurationLevel existing : new ConfigurationLevel[]{ConfigurationLevel.GLOBAL, ConfigurationLevel.CLUSTER, ConfigurationLevel.INSTANCE}) {
+                if(existing.compareTo(level) > 0) {
+                    String exvalue = getConfiguration(key,existing);
+                    if(exvalue != null) {
+                        log.info("ZOOKEEPER: ignoring configuration option {}, because there is a more specific configuration on level {}", key, existing);
+                        return;
+                    }
+                }
+            }
+
+            log.info("ZOOKEEPER: setting configuration option {} = {}", key, value);
+            configurationService.setConfiguration(key,value);
+        } catch (InterruptedException | NodeKeeperException | IOException e) {
+            log.error("ZOOKEEPER: error reading Zookeeper configuration",e);
+        }
+    }
+
+    @Override
+    public void onNodeUpdated(Node<String> node) {
+        onNodeCreated(node);
+    }
+
+    @Override
+    public void onNodeDeleted(Node<String> node) {
+        ConfigurationLevel level = getConfigurationLevel(node);
+        String key = node.getPath().substring(node.getPath().lastIndexOf("/")+1);
+
+        try {
+            // look if there is a more specific existing configuration; if yes, issue a message and return
+            for(ConfigurationLevel existing : new ConfigurationLevel[]{ConfigurationLevel.GLOBAL, ConfigurationLevel.CLUSTER, ConfigurationLevel.INSTANCE}) {
+                if(existing.compareTo(level) > 0) {
+                    String exvalue = getConfiguration(key,existing);
+                    if(exvalue != null) {
+                        log.info("ZOOKEEPER: ignoring delete option {}, because there is a more specific configuration on level {}", key, existing);
+                        return;
+                    }
+                }
+            }
+
+            // look if there is a more generic existing configuration; if yes, use this configuration instead and return
+            for(ConfigurationLevel existing : new ConfigurationLevel[]{ConfigurationLevel.INSTANCE, ConfigurationLevel.CLUSTER, ConfigurationLevel.GLOBAL}) {
+                if(existing.compareTo(level) < 0) {
+                    String exvalue = getConfiguration(key,existing);
+                    if(exvalue != null) {
+                        log.info("ZOOKEEPER: iupdating option {} to its more generic value {} at level {}", key, exvalue, existing);
+                        configurationService.setConfiguration(key,exvalue);
+                        return;
+                    }
+                }
+            }
+
+
+            log.info("ZOOKEEPER: deleting configuration option {}", key);
+            configurationService.removeConfiguration(key);
+        } catch (InterruptedException | NodeKeeperException | IOException e) {
+            log.error("ZOOKEEPER: error reading Zookeeper configuration",e);
+        }
+    }
+
+    @Override
+    public Class<String> getType() {
+        return String.class;
+    }
+
+    /**
+     * Return the configuration value for the given key at the given level if it exists, or return null otherwise.
+     *
+     * @param key
+     * @param level
+     * @return
+     */
+    private String getConfiguration(String key, ConfigurationLevel level) throws InterruptedException, IOException, NodeKeeperException {
+        String uuid = configurationService.getStringConfiguration(ZookeeperService.ZK_INSTANCE, UUID.randomUUID().toString());
+        String cluster = configurationService.getStringConfiguration(ZookeeperService.ZK_CLUSTER, "default");
+
+        String path;
+        switch (level) {
+            case INSTANCE:
+                path = String.format("/marmotta/clusters/%s/instances/%s/config/%s", cluster, uuid, key);
+                break;
+            case CLUSTER:
+                path = String.format("/marmotta/clusters/%s/config/%s", cluster, key);
+                break;
+            default:
+                path = String.format("/marmotta/config/%s", key);
+                break;
+        }
+
+        Node<String> node = nodekeeper.readNode(path, String.class);
+
+        if(node != null) {
+            return node.getData();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Get the configuration level of the node
+     *
+     * @param node
+     * @return
+     */
+    private ConfigurationLevel getConfigurationLevel(Node<String> node) {
+        if(node.getPath().contains("instances")) {
+            return ConfigurationLevel.INSTANCE;
+        } else if(node.getPath().contains("clusters")) {
+            return ConfigurationLevel.CLUSTER;
+        } else {
+            return ConfigurationLevel.GLOBAL;
+        }
+    }
+
+    /**
+     * Used for representing the different levels at which a configuration event can happen; if there is an
+     * INSTANCE level configuration, it will override CLUSTER and GLOBAL and so on.
+     */
+    private static enum ConfigurationLevel {
+        GLOBAL, CLUSTER, INSTANCE
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/services/ZookeeperServiceImpl.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/services/ZookeeperServiceImpl.java b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/services/ZookeeperServiceImpl.java
new file mode 100644
index 0000000..4370c69
--- /dev/null
+++ b/platform/marmotta-zookeeper/src/main/java/org/apache/marmotta/platform/zookeeper/services/ZookeeperServiceImpl.java
@@ -0,0 +1,193 @@
+/*
+ * 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.marmotta.platform.zookeeper.services;
+
+import at.salzburgresearch.nodekeeper.NodeKeeper;
+import at.salzburgresearch.nodekeeper.exception.NodeKeeperException;
+import org.apache.commons.lang.StringUtils;
+import org.apache.marmotta.platform.core.api.config.ConfigurationService;
+import org.apache.marmotta.platform.core.events.ConfigurationServiceInitEvent;
+import org.apache.marmotta.platform.zookeeper.api.ZookeeperService;
+import org.apache.marmotta.platform.zookeeper.event.ZookeeperInitEvent;
+import org.apache.marmotta.platform.zookeeper.listeners.ConfigurationListener;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooDefs;
+import org.slf4j.Logger;
+
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Event;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Any;
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.UUID;
+
+/**
+ * Add file description here!
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+@ApplicationScoped
+public class ZookeeperServiceImpl implements ZookeeperService {
+
+    @Inject
+    private Logger log;
+
+    @Inject
+    private ConfigurationService configurationService;
+
+    @Inject
+    @Any
+    private Event<ZookeeperInitEvent> zookeeperInitEvent;
+
+    private NodeKeeper nodeKeeper;
+
+    private Properties properties;
+
+
+    public void initialise(@Observes ConfigurationServiceInitEvent event) throws IOException, InterruptedException {
+        log.warn("Activating Marmotta Zookeeper Bridge");
+
+        //read configuration
+        final String connectionString = configurationService.getContextParam(ZK_SERVER);
+
+        if(connectionString != null) {
+
+            log.info(" - connection string: {}",connectionString);
+
+            this.properties = new Properties();
+            File nkProperties = new File(configurationService.getHome() + File.separator + "nodekeeper.properties");
+            if(nkProperties.exists()) {
+                properties.load(new FileInputStream(nkProperties));
+            }
+
+            // get instance id (if not configured), first try from context parameters, if not given create random
+            if(StringUtils.isBlank(configurationService.getStringConfiguration(ZK_INSTANCE))) {
+                String instanceId;
+                if(StringUtils.isBlank(configurationService.getContextParam(ZK_INSTANCE))) {
+                    instanceId = UUID.randomUUID().toString();
+                } else {
+                    instanceId = configurationService.getContextParam(ZK_INSTANCE);
+                }
+
+                configurationService.setConfiguration(ZK_INSTANCE, instanceId);
+            }
+
+            // get cluster name (if not configured), first try from context parameters, if not given use "default"
+            if(StringUtils.isBlank(configurationService.getStringConfiguration(ZK_CLUSTER))) {
+                String clusterId;
+                if(StringUtils.isBlank(configurationService.getContextParam(ZK_CLUSTER))) {
+                    clusterId = "default";
+                } else {
+                    clusterId = configurationService.getContextParam(ZK_CLUSTER);
+                }
+
+                configurationService.setConfiguration(ZK_CLUSTER, clusterId);
+            }
+
+
+            log.info(" - initialize nodekeeper connection for instance {} (cluster {})",configurationService.getStringConfiguration(ZK_INSTANCE), configurationService.getStringConfiguration(ZK_CLUSTER));
+
+
+
+            try {
+                nodeKeeper = new NodeKeeper(connectionString,configurationService.getIntConfiguration(ZK_TIMEOUT, 60000),properties, null);
+
+                initZooKeeper();
+
+                String uuid = configurationService.getStringConfiguration(ZK_INSTANCE, UUID.randomUUID().toString());
+                String cluster = configurationService.getStringConfiguration(ZK_CLUSTER, "default");
+
+                ConfigurationListener listener = new ConfigurationListener(configurationService, nodeKeeper);
+
+                nodeKeeper.addListener("/marmotta/config/[^/]+", listener );
+                nodeKeeper.addListener(String.format("/marmotta/clusters/%s/config/[^/]+", cluster), listener );
+                nodeKeeper.addListener(String.format("/marmotta/clusters/%s/instances/%s/config/[^/]+", cluster, uuid), listener );
+
+                nodeKeeper.startListeners();
+
+                log.info("... running");
+
+                zookeeperInitEvent.fire(new ZookeeperInitEvent());
+            } catch (KeeperException ex) {
+                log.error("could not initialise Zookeeper: {}", ex.getMessage());
+            } catch (NodeKeeperException e) {
+                log.error("could not initialise NodeKeeper: {}", e.getMessage());
+            }
+        } else {
+            log.warn("no Zookeeper servers configured, Zookeeper Integration not available");
+        }
+
+    }
+
+    @PreDestroy
+    private void shutdown() throws InterruptedException, IOException {
+        log.info("deactivating Zookeeper Bridge ...");
+        if(nodeKeeper != null) {
+            log.info(" - closing nodekeeper connection");
+            nodeKeeper.shutdown();
+            log.info("   ... closed");
+        }
+        File nkProperties = new File(configurationService.getHome() + File.separator + "nodekeeper.properties");
+        properties.store(new FileOutputStream(nkProperties), "automatic nodekeeper state");
+    }
+
+    /**
+     * init zookeeper if necessary
+     * @throws org.apache.zookeeper.KeeperException
+     * @throws InterruptedException
+     */
+    private void initZooKeeper() throws KeeperException, InterruptedException, IOException {
+
+        if (nodeKeeper == null || nodeKeeper.getZooKeeper() == null) {
+            log.error("ZooKeeper not available");
+        } else {
+            String uuid = configurationService.getStringConfiguration(ZK_INSTANCE, UUID.randomUUID().toString());
+            String cluster = configurationService.getStringConfiguration(ZK_CLUSTER, "default");
+
+            //create base paths
+            createNodeIfNotExists("/marmotta");
+            createNodeIfNotExists("/marmotta/config");
+
+            createNodeIfNotExists("/marmotta/clusters");
+            createNodeIfNotExists(String.format("/marmotta/clusters/%s", cluster));
+            createNodeIfNotExists(String.format("/marmotta/clusters/%s/config", cluster));
+
+            createNodeIfNotExists(String.format("/marmotta/clusters/%s/instances", cluster));
+            createNodeIfNotExists(String.format("/marmotta/clusters/%s/instances/%s", cluster, uuid));
+            createNodeIfNotExists(String.format("/marmotta/clusters/%s/instances/%s/config", cluster, uuid));
+        }
+    }
+
+
+    private void createNodeIfNotExists(String path) throws KeeperException, InterruptedException {
+        if (nodeKeeper.getZooKeeper().exists(path,false) == null) {
+            String uuid = configurationService.getStringConfiguration(ZK_INSTANCE, UUID.randomUUID().toString());
+
+            nodeKeeper.getZooKeeper().create(path,new String("creator:"+uuid).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-zookeeper/src/main/resources/META-INF/beans.xml
----------------------------------------------------------------------
diff --git a/platform/marmotta-zookeeper/src/main/resources/META-INF/beans.xml b/platform/marmotta-zookeeper/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..461858e
--- /dev/null
+++ b/platform/marmotta-zookeeper/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans
+   xmlns="http://java.sun.com/xml/ns/javaee"
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="
+      http://java.sun.com/xml/ns/javaee
+      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/marmotta-zookeeper/src/main/resources/kiwi-module.properties
----------------------------------------------------------------------
diff --git a/platform/marmotta-zookeeper/src/main/resources/kiwi-module.properties b/platform/marmotta-zookeeper/src/main/resources/kiwi-module.properties
new file mode 100644
index 0000000..dd81f71
--- /dev/null
+++ b/platform/marmotta-zookeeper/src/main/resources/kiwi-module.properties
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+name=Zookeeper Integration
+
+container=Cloud
+container.weight = 10
+
+subtitle = Configure Zookeeper
+weight = 10
+
+icon_small = /admin/img/config_small.png
+
+#do not change!!!
+baseurl=/zookeeper
+
+adminpage.0.title=About
+adminpage.0.link=/admin/about.html
+
+adminpage.1.title=Configuration
+adminpage.1.link=/admin/configuration.html
+
+
+webservices=

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/eea9f094/platform/pom.xml
----------------------------------------------------------------------
diff --git a/platform/pom.xml b/platform/pom.xml
index 4561bc5..20f3325 100644
--- a/platform/pom.xml
+++ b/platform/pom.xml
@@ -70,6 +70,8 @@
         <module>marmotta-reasoner-kiwi</module>
         <module>marmotta-versioning-kiwi</module>
 
+        <!-- Cloud Integration -->
+        <module>marmotta-zookeeper</module>
 
     </modules>