You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by an...@apache.org on 2016/01/08 12:52:32 UTC

[1/3] incubator-tamaya git commit: TAMAYA-133: Added support for multiple server urls and ensured connection/socket timeouts (configurable), when doing Round Robin internally.

Repository: incubator-tamaya
Updated Branches:
  refs/heads/master ba5d9571f -> 846842cb2


TAMAYA-133: Added support for multiple server urls and ensured connection/socket timeouts (configurable), when doing Round Robin internally.


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

Branch: refs/heads/master
Commit: 66c28e85328f4dad8e4d0d03c58e80de04b8b4ce
Parents: ba5d957
Author: anatole <an...@apache.org>
Authored: Fri Jan 8 12:42:41 2016 +0100
Committer: anatole <an...@apache.org>
Committed: Fri Jan 8 12:42:41 2016 +0100

----------------------------------------------------------------------
 .../org/apache/tamaya/etcd/EtcdAccessor.java    |  52 ++++++---
 .../apache/tamaya/etcd/EtcdPropertySource.java  | 116 ++++++++++++++-----
 .../apache/tamaya/etcd/EtcdAccessorTest.java    |   6 +-
 .../tamaya/etcd/EtcdPropertySourceTest.java     |   2 +-
 4 files changed, 124 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/66c28e85/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java
----------------------------------------------------------------------
diff --git a/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java b/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java
index d7c0f0c..cd4724a 100644
--- a/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java
+++ b/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java
@@ -21,6 +21,7 @@ package org.apache.tamaya.etcd;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpStatus;
 import org.apache.http.NameValuePair;
+import org.apache.http.client.config.RequestConfig;
 import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpDelete;
@@ -28,6 +29,7 @@ import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPut;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.util.EntityUtils;
 
@@ -53,6 +55,8 @@ public class EtcdAccessor {
 
     private static final Logger LOG = Logger.getLogger(EtcdAccessor.class.getName());
 
+    /** Timeout in seconds. */
+    private static int timeout = 2;
     /** Property that make Johnzon accept commentc. */
     public static final String JOHNZON_SUPPORTS_COMMENTS_PROP = "org.apache.johnzon.supports-comments";
     /** The JSON reader factory used. */
@@ -71,20 +75,16 @@ public class EtcdAccessor {
     private CloseableHttpClient httpclient = HttpClients.createDefault();
 
     /**
-     * Creates a new instance, trying to read the basic server endpoint from {@code -Detcd.url}, using default
-     * value of {@code http://127.0.0.1:4001}.
+     * Creates a new instance with the basic access url.
+     * @param server server url, e.g. {@code http://127.0.0.1:4001}, not null.
      * @throws MalformedURLException
      */
-    public EtcdAccessor() throws MalformedURLException {
-        this(System.getProperty("etcd.url", "http://127.0.0.1:4001"));
+    public EtcdAccessor(String server)throws MalformedURLException{
+        this(server, 2);
     }
 
-    /**
-     * Creates a new instance with the basic access url.
-     * @param server server url, e.g. {@code http://127.0.0.1:4001}.
-     * @throws MalformedURLException
-     */
-    public EtcdAccessor(String server) throws MalformedURLException {
+    public EtcdAccessor(String server, int timeout) throws MalformedURLException {
+        this.timeout = timeout;
         if(server.endsWith("/")){
             serverURL = server.substring(0, server.length()-1);
         } else{
@@ -103,6 +103,8 @@ public class EtcdAccessor {
         try {
             CloseableHttpClient httpclient = HttpClients.createDefault();
             HttpGet httpGet = new HttpGet(serverURL + "/version");
+            httpGet.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(timeout)
+                    .setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).build());
             response = httpclient.execute(httpGet);
             HttpEntity entity = null;
             if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
@@ -113,7 +115,7 @@ public class EtcdAccessor {
             }
             return version;
         } catch(Exception e){
-            LOG.log(Level.SEVERE, "Error getting etcd version from: " + serverURL, e);
+            LOG.log(Level.INFO, "Error getting etcd version from: " + serverURL, e);
         } finally {
             if(response!=null){
                 try {
@@ -156,6 +158,8 @@ public class EtcdAccessor {
         Map<String,String> result = new HashMap<>();
         try {
             HttpGet httpGet = new HttpGet(serverURL + "/v2/keys/"+key);
+            httpGet.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(timeout)
+                    .setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).build());
             response = httpclient.execute(httpGet);
             if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                 HttpEntity entity = response.getEntity();
@@ -181,7 +185,7 @@ public class EtcdAccessor {
                 result.put("_" + key +".NOT_FOUND.target", "[etcd]"+serverURL);
             }
         } catch(Exception e){
-            LOG.log(Level.SEVERE, "Error reading key '"+key+"' from etcd: " + serverURL, e);
+            LOG.log(Level.INFO, "Error reading key '"+key+"' from etcd: " + serverURL, e);
             result.put("_ERROR", "Error reading key '"+key+"' from etcd: " + serverURL + ": " + e.toString());
         } finally {
             if(response!=null){
@@ -249,6 +253,8 @@ public class EtcdAccessor {
         Map<String,String> result = new HashMap<>();
         try{
             HttpPut put = new HttpPut(serverURL + "/v2/keys/"+key);
+            put.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(timeout)
+                    .setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).build());
             List<NameValuePair> nvps = new ArrayList<>();
             nvps.add(new BasicNameValuePair("value", value));
             if(ttlSeconds!=null){
@@ -295,7 +301,7 @@ public class EtcdAccessor {
                 EntityUtils.consume(entity);
             }
         } catch(Exception e){
-            LOG.log(Level.SEVERE, "Error writing to etcd: " + serverURL, e);
+            LOG.log(Level.INFO, "Error writing to etcd: " + serverURL, e);
             result.put("_ERROR", "Error writing '"+key+"' to etcd: " + serverURL + ": " + e.toString());
         } finally {
             if(response!=null){
@@ -333,6 +339,8 @@ public class EtcdAccessor {
         Map<String,String> result = new HashMap<>();
         try{
             HttpDelete delete = new HttpDelete(serverURL + "/v2/keys/"+key);
+            delete.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(timeout)
+                    .setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).build());
             List<NameValuePair> nvps = new ArrayList<>();
             // delete.setEntity(new UrlEncodedFormEntity(nvps));
             response = httpclient.execute(delete);
@@ -372,14 +380,14 @@ public class EtcdAccessor {
                 EntityUtils.consume(entity);
             }
         } catch(Exception e){
-            LOG.log(Level.SEVERE, "Error deleting key '"+key+"' from etcd: " + serverURL, e);
+            LOG.log(Level.INFO, "Error deleting key '"+key+"' from etcd: " + serverURL, e);
             result.put("_ERROR", "Error deleting '"+key+"' from etcd: " + serverURL + ": " + e.toString());
         } finally {
             if(response!=null){
                 try {
                     response.close();
                 } catch (IOException e) {
-                    // TODO log error
+                    LOG.log(Level.WARNING, "Failed to close http response", e);
                 }
             }
         }
@@ -446,6 +454,8 @@ public class EtcdAccessor {
         Map<String,String> result = new HashMap<>();
         try{
             HttpGet get = new HttpGet(serverURL + "/v2/keys/"+directory+"?recursive="+recursive);
+            get.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(timeout)
+                    .setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).build());
             response = httpclient.execute(get);
             if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                 HttpEntity entity = response.getEntity();
@@ -458,14 +468,14 @@ public class EtcdAccessor {
                 EntityUtils.consume(entity);
             }
         } catch(Exception e){
-            LOG.log(Level.SEVERE, "Error reading properties for '"+directory+"' from etcd: " + serverURL, e);
+            LOG.log(Level.INFO, "Error reading properties for '"+directory+"' from etcd: " + serverURL, e);
             result.put("_ERROR", "Error reading properties for '"+directory+"' from etcd: " + serverURL + ": " + e.toString());
         } finally {
             if(response!=null){
                 try {
                     response.close();
                 } catch (IOException e) {
-                    // TODO log error
+                    LOG.log(Level.WARNING, "Failed to close http response", e);
                 }
             }
         }
@@ -504,5 +514,11 @@ public class EtcdAccessor {
         }
     }
 
-
+    /**
+     * Access the server root URL used by this accessor.
+     * @return
+     */
+    public String getUrl() {
+        return serverURL;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/66c28e85/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java
----------------------------------------------------------------------
diff --git a/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java b/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java
index 7d76748..3784a38 100644
--- a/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java
+++ b/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java
@@ -20,24 +20,37 @@ package org.apache.tamaya.etcd;
 
 import org.apache.tamaya.spi.PropertySource;
 
-import java.net.MalformedURLException;
-import java.util.Collections;
-import java.util.Map;
+import java.util.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
- * Propertysource that is reading configuration from a configured etcd endpoint.
+ * Propertysource that is reading configuration from a configured etcd endpoint. Setting
+ * {@code etcd.prefix} as system property maps the etcd based onfiguration
+ * to this prefix namespace. Etcd servers are configured as {@code etcd.server.urls} system or environment property.
  */
 public class EtcdPropertySource implements PropertySource{
-
-    private EtcdAccessor etcdBackend;
+    private static final Logger LOG = Logger.getLogger(EtcdPropertySource.class.getName());
+    private List<EtcdAccessor> etcdBackends = new ArrayList<>();
+    private String prefix = System.getProperty("etcd.prefix", "");
 
     public EtcdPropertySource(){
-        try {
-            etcdBackend = new EtcdAccessor();
-        } catch (MalformedURLException e) {
-            e.printStackTrace();
+        int timeout = 2;
+        String val = System.getProperty("etcd.timeout");
+        if(val == null){
+            val = System.getenv("etcd.timeout");
+        }
+        if(val!=null){
+            timeout = Integer.parseInt(val);
+        }
+        String serverURLs = System.getProperty("etcd.server.urls", "http://127.0.0.1:4001");
+        for(String url:serverURLs.split("\\,")) {
+            try{
+                etcdBackends.add(new EtcdAccessor(url, timeout));
+                LOG.info("Using etcd endoint: " + url);
+            } catch(Exception e){
+                LOG.log(Level.SEVERE, "Error initializing etcd accessor for URL: " + url, e);
+            }
         }
     }
 
@@ -70,38 +83,81 @@ public class EtcdPropertySource implements PropertySource{
 
     @Override
     public String get(String key) {
-        if(etcdBackend!=null) {
-            Map<String,String> props = null;
-            if(key.startsWith("_")){
-                String reqKey = key.substring(1);
-                if(reqKey.endsWith(".createdIndex")){
-                    reqKey = reqKey.substring(0,reqKey.length()-".createdIndex".length());
-                } else if(reqKey.endsWith(".modifiedIndex")){
-                    reqKey = reqKey.substring(0,reqKey.length()-".modifiedIndex".length());
-                } else if(reqKey.endsWith(".ttl")){
-                    reqKey = reqKey.substring(0,reqKey.length()-".ttl".length());
-                } else if(reqKey.endsWith(".expiration")){
-                    reqKey = reqKey.substring(0,reqKey.length()-".expiration".length());
-                } else if(reqKey.endsWith(".source")){
-                    reqKey = reqKey.substring(0,reqKey.length()-".source".length());
+        // check prefix, if key does not start with it, it is not part of our name space
+        // if so, the prefix part must be removed, so etcd can resolve without it
+        if(!key.startsWith(prefix)){
+            return null;
+        }
+        else{
+            key = key.substring(prefix.length());
+        }
+        Map<String,String> props = null;
+        String reqKey = key;
+        if(key.startsWith("_")){
+            reqKey = key.substring(1);
+            if(reqKey.endsWith(".createdIndex")){
+                reqKey = reqKey.substring(0,reqKey.length()-".createdIndex".length());
+            } else if(reqKey.endsWith(".modifiedIndex")){
+                reqKey = reqKey.substring(0,reqKey.length()-".modifiedIndex".length());
+            } else if(reqKey.endsWith(".ttl")){
+                reqKey = reqKey.substring(0,reqKey.length()-".ttl".length());
+            } else if(reqKey.endsWith(".expiration")){
+                reqKey = reqKey.substring(0,reqKey.length()-".expiration".length());
+            } else if(reqKey.endsWith(".source")){
+                reqKey = reqKey.substring(0,reqKey.length()-".source".length());
+            }
+        }
+        for(EtcdAccessor accessor:etcdBackends){
+            try{
+                props = accessor.get(key);
+                if(!props.containsKey("_ERROR")) {
+                    // No repfix mapping necessary here, since we only access/return the value...
+                    return props.get(key);
+                } else{
+                    LOG.log(Level.FINE, "etcd error on " + accessor.getUrl() + ": " + props.get("_ERROR"));
                 }
-                props = etcdBackend.get(reqKey);
-            } else{
-                props = etcdBackend.get(key);
+            } catch(Exception e){
+                LOG.log(Level.FINE, "etcd access failed on " + accessor.getUrl() + ", trying next...", e);
             }
-            return props.get(key);
         }
         return null;
     }
 
     @Override
     public Map<String, String> getProperties() {
-        if(etcdBackend!=null) {
-            return etcdBackend.getProperties("");
+        if(etcdBackends.isEmpty()){
+            for(EtcdAccessor accessor:etcdBackends){
+                try{
+                    Map<String, String> props = accessor.getProperties("");
+                    if(!props.containsKey("_ERROR")) {
+                        return mapPrefix(props);
+                    } else{
+                        LOG.log(Level.FINE, "etcd error on " + accessor.getUrl() + ": " + props.get("_ERROR"));
+                    }
+                } catch(Exception e){
+                    LOG.log(Level.FINE, "etcd access failed on " + accessor.getUrl() + ", trying next...", e);
+                }
+            }
         }
         return Collections.emptyMap();
     }
 
+    private Map<String, String> mapPrefix(Map<String, String> props) {
+        if(prefix.isEmpty()){
+            return props;
+        }
+        Map<String,String> map = new HashMap<>();
+        for(Map.Entry<String,String> entry:props.entrySet()){
+            if(entry.getKey().startsWith("_")){
+                map.put("_" + prefix + entry.getKey().substring(1), entry.getValue());
+            }
+            else{
+                map.put(prefix+ entry.getKey(), entry.getValue());
+            }
+        }
+        return map;
+    }
+
     @Override
     public boolean isScannable() {
         return true;

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/66c28e85/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdAccessorTest.java
----------------------------------------------------------------------
diff --git a/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdAccessorTest.java b/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdAccessorTest.java
index e3b4196..80bd716 100644
--- a/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdAccessorTest.java
+++ b/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdAccessorTest.java
@@ -37,10 +37,10 @@ public class EtcdAccessorTest {
 
     @BeforeClass
     public static void setup() throws MalformedURLException {
-        accessor = new EtcdAccessor();
+        accessor = new EtcdAccessor("http://192.168.99.105:4001");
         if(!accessor.getVersion().contains("etcd")){
-            System.out.println("Disabling etcd tests, etcd not accessible at: " + System.getProperty("etcd.url"));
-            System.out.println("Configure etcd with -Detcd.url=http://<IP>:<PORT>");
+            System.out.println("Disabling etcd tests, etcd not accessible at: " + System.getProperty("etcd.server.urls"));
+            System.out.println("Configure etcd with -Detcd.server.urls=http://<IP>:<PORT>");
         }
         else{
             execute = true;

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/66c28e85/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdPropertySourceTest.java
----------------------------------------------------------------------
diff --git a/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdPropertySourceTest.java b/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdPropertySourceTest.java
index 590dc12..da4f6e6 100644
--- a/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdPropertySourceTest.java
+++ b/modules/integration/etcd/src/test/java/org/apache/tamaya/etcd/EtcdPropertySourceTest.java
@@ -35,7 +35,7 @@ public class EtcdPropertySourceTest {
 
     @BeforeClass
     public static void setup(){
-        System.setProperty("etcd.url", "http://192.168.99.105:4001");
+        System.setProperty("etcd.server.urls", "http://8.8.8.8:4001,http://192.168.99.105:4001");
     }
 
     @Test


[2/3] incubator-tamaya git commit: TAMAYA-133: Added tamaya prefix for system and env properties.

Posted by an...@apache.org.
TAMAYA-133: Added tamaya prefix for system and env properties.


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

Branch: refs/heads/master
Commit: 895dfd84da356ce04bc5a3bb0d51207205f56eb5
Parents: 66c28e8
Author: anatole <an...@apache.org>
Authored: Fri Jan 8 12:51:18 2016 +0100
Committer: anatole <an...@apache.org>
Committed: Fri Jan 8 12:51:18 2016 +0100

----------------------------------------------------------------------
 .../org/apache/tamaya/etcd/EtcdPropertySource.java  | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/895dfd84/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java
----------------------------------------------------------------------
diff --git a/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java b/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java
index 3784a38..64e9c6d 100644
--- a/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java
+++ b/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java
@@ -32,21 +32,27 @@ import java.util.logging.Logger;
 public class EtcdPropertySource implements PropertySource{
     private static final Logger LOG = Logger.getLogger(EtcdPropertySource.class.getName());
     private List<EtcdAccessor> etcdBackends = new ArrayList<>();
-    private String prefix = System.getProperty("etcd.prefix", "");
+    private String prefix = System.getProperty("tamaya.etcd.prefix", "");
 
     public EtcdPropertySource(){
         int timeout = 2;
-        String val = System.getProperty("etcd.timeout");
+        String val = System.getProperty("tamaya.etcd.timeout");
         if(val == null){
-            val = System.getenv("etcd.timeout");
+            val = System.getenv("tamaya.etcd.timeout");
         }
         if(val!=null){
             timeout = Integer.parseInt(val);
         }
-        String serverURLs = System.getProperty("etcd.server.urls", "http://127.0.0.1:4001");
+        String serverURLs = System.getProperty("tamaya.etcd.server.urls");
+        if(serverURLs==null){
+            serverURLs = System.getenv("tamaya.etcd.server.urls");
+        }
+        if(serverURLs==null){
+            serverURLs = "http://127.0.0.1:4001";
+        }
         for(String url:serverURLs.split("\\,")) {
             try{
-                etcdBackends.add(new EtcdAccessor(url, timeout));
+                etcdBackends.add(new EtcdAccessor(url.trim(), timeout));
                 LOG.info("Using etcd endoint: " + url);
             } catch(Exception e){
                 LOG.log(Level.SEVERE, "Error initializing etcd accessor for URL: " + url, e);


[3/3] incubator-tamaya git commit: TAMAYA-133: Added docu for etcd support.

Posted by an...@apache.org.
TAMAYA-133: Added docu for etcd support.


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

Branch: refs/heads/master
Commit: 846842cb24daed70157dd921b2a203205ff0bb80
Parents: 895dfd8
Author: anatole <an...@apache.org>
Authored: Fri Jan 8 12:52:25 2016 +0100
Committer: anatole <an...@apache.org>
Committed: Fri Jan 8 12:52:25 2016 +0100

----------------------------------------------------------------------
 src/site/asciidoc/extensions/index.adoc    |   1 +
 src/site/asciidoc/extensions/mod_etcd.adoc | 221 ++++++++++++++++++++++++
 2 files changed, 222 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/846842cb/src/site/asciidoc/extensions/index.adoc
----------------------------------------------------------------------
diff --git a/src/site/asciidoc/extensions/index.adoc b/src/site/asciidoc/extensions/index.adoc
index 87d8df7..9607d80 100644
--- a/src/site/asciidoc/extensions/index.adoc
+++ b/src/site/asciidoc/extensions/index.adoc
@@ -84,6 +84,7 @@ from a sledgehammer to a scalpell:
 |+org.apache.tamaya.ext:tamaya-camel+       | Integration for Apache Camel.                | link:mod_camel.html[Documentation]
 |+org.apache.tamaya.ext:tamaya-spring+      | Integration for Spring / Spring Boot.        | link:mod_spring.html[Documentation]
 |+org.apache.tamaya.ext:tamaya-osgi+        | Integration for OSGI containers.             | link:mod_osgi.html[Documentation]
+|+org.apache.tamaya.ext:tamaya-etcd+        | Integration with etcd clusters.              | link:mod_etcd.html[Documentation]
 |=======
 
 

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/846842cb/src/site/asciidoc/extensions/mod_etcd.adoc
----------------------------------------------------------------------
diff --git a/src/site/asciidoc/extensions/mod_etcd.adoc b/src/site/asciidoc/extensions/mod_etcd.adoc
new file mode 100644
index 0000000..22247fd
--- /dev/null
+++ b/src/site/asciidoc/extensions/mod_etcd.adoc
@@ -0,0 +1,221 @@
+= Apache Tamaya -- Extension: Integration with etcd (Core OS)
+
+:name: Tamaya
+:rootpackage: org.apache.tamaya.integration.etcd
+:title: Apache Tamaya Extension: Integration with etcd service
+:revdate: January 2016
+:authorinitials: ATR
+:author: Anatole Tresch
+:email: <an...@apache.org>
+:source-highlighter: coderay
+:website: http://tamaya.incubator.apache.org/
+:toc:
+:toc-placement: manual
+:encoding: UTF-8
+:numbered:
+// 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.
+'''
+
+<<<
+
+toc::[]
+
+<<<
+:numbered!:
+<<<
+[[Optional]]
+== Integration with etcd (Extension Module)
+=== Overview
+
+The Tamaya etcd integration module provides different artifacts which allows integration of Apachae Tamaya
+configuration with etcd. Basically the module supports read-only integration.
+
+NOTE: The etcd backend itself also has methods that allow writing to the etcd cluster back. Once Tamaya's writable
+      feature will be ready for broader usage (currently it is in the sandbox).
+
+
+=== Compatibility
+
+The module is based on Java 7, so it will not run on Java 7 and beyond.
+
+
+=== Installation
+
+To benefit from configuration builder support you only must add the corresponding dependency to your module:
+
+[source, xml]
+-----------------------------------------------
+<dependency>
+  <groupId>org.apache.tamaya.ext</groupId>
+  <artifactId>tamaya-etcd</artifactId>
+  <version>{tamayaVersion}</version>
+</dependency>
+-----------------------------------------------
+
+
+=== The Extensions Provided
+
+ETcd integration comes basically with 2 artifacts:
+
+* The +org.apache.tamaya.etcd.EtcdAccessor+ can be configured with a an url targeting an etcd server's REST endpoint root.
+  (+org.apache.tamaya.etcd.EtcdAccessor+). The accessor basically provides a simple Java API for communicating
+  with etcd server. The accessor hereby allows reading of single properties, or whole subtrees. Also the basic non
+  atomic write methods are implemented.
+* The +org.apache.tamaya.etcd.EtcdPropertySource+ is a +PropertySource+ with a default ordinal of 100 and the name 'etcd', which is
+  automatically registered.
+
+=== The EtcdAccessor
+
+The accessor mentioned implements the basic read and write API for communicating with an etcd configuration cluster.
+Hereby the accessor also provides etcd specific data such as +createdIndex, modifiedIndex, ttl+ in the +Map+
+returned. Hereby the concept of etcd is used where keys starting with an '_' will be hidden from the overall
+properties map, being only directly/explicitly accessible:
+
+[source, java]
+-----------------------------------------------
+public class EtcdAccessor {
+
+    /**
+     * Creates a new instance with the basic access url.
+     * @param server server url, e.g. {@code http://127.0.0.1:4001}.
+     * @throws MalformedURLException
+     */
+    public EtcdAccessor(String server) throws MalformedURLException;
+
+    /**
+     * Get the etcd server version.
+     * @return the etcd server version, never null.
+     */
+    public String getVersion();
+
+    /**
+     * Ask etcd for s aingle key, value pair. Hereby the response returned from etcd:
+     * <pre>
+     *     key=value
+     *     _key.source=[etcd]http://127.0.0.1:4001
+     *     _key.createdIndex=12
+     *     _key.modifiedIndex=34    // optional
+     *     _key.ttl=300             // optional
+     *     _key.expiration=...      // optional
+     * </pre>
+     * @param key the requested key
+     * @return the mapped result, including meta-entries.
+     */
+    public Map<String,String> get(String key);
+
+    /**
+     * Creates/updates an entry in etcd without any ttl set.
+     * The response is as follows:
+     * <pre>
+     *     key=value
+     *     _key.source=[etcd]http://127.0.0.1:4001
+     *     _key.createdIndex=12
+     *     _key.modifiedIndex=34             // optional
+     *     _key.prevNode.createdIndex=12     // optional
+     *     _key.prevNode.modifiedIndex=34    // optional
+     * </pre>
+     * @param key the property key, not null
+     * @param value the value to be set
+     * @return the result map as described above.
+     */
+    public Map<String,String> set(String key, String value);
+
+    /**
+     * Creates/updates an entry in etcd. The response is as follows:
+     * <pre>
+     *     key=value
+     *     _key.source=[etcd]http://127.0.0.1:4001
+     *     _key.createdIndex=12
+     *     _key.modifiedIndex=34             // optional
+     *     _key.ttl=300                      // optional
+     *     _key.expiry=...                   // optional
+     *     _key.prevNode.createdIndex=12     // optional
+     *     _key.prevNode.modifiedIndex=34    // optional
+     *     _key.prevNode.ttl=300             // optional
+     *     _key.prevNode.expiration=...      // optional
+     * </pre>
+     * @param key the property key, not null
+     * @param value the value to be set
+     * @param ttlSeconds the ttl in seconds (optional)
+     * @return the result map as described above.
+     */
+    public Map<String,String> set(String key, String value, Integer ttlSeconds);
+
+
+    /**
+     * Deletes a given key. The response is as follows:
+     * <pre>
+     *     _key.source=[etcd]http://127.0.0.1:4001
+     *     _key.createdIndex=12
+     *     _key.modifiedIndex=34
+     *     _key.ttl=300                       // optional
+     *     _key.expiry=...                    // optional
+     *     _key.prevNode.createdIndex=12      // optional
+     *     _key.prevNode.modifiedIndex=34     // optional
+     *     _key.prevNode.ttl=300              // optional
+     *     _key.prevNode.expiration=...       // optional
+     *     _key.prevNode.value=...            // optional
+     * </pre>
+     * @param key the key to be deleted.
+     * @return the response mpas as described above.
+     */
+    public Map<String,String> delete(String key);
+
+
+    /**
+     * Access regular Tamaya properties map as follows:
+     * <pre>
+     *    key1=myvalue
+     *     _key1.source=[etcd]http://127.0.0.1:4001
+     *     _key1.createdIndex=12
+     *     _key1.modifiedIndex=34          // optional
+     *     _key1.ttl=300                   // optional
+     *     _key1.expiration=...            // optional
+     *
+     *      key2=myvaluexxx
+     *     _key2.source=[etcd]http://127.0.0.1:4001
+     *     _key2.createdIndex=12
+     *
+     *      key3=val3
+     *     _key3.source=[etcd]http://127.0.0.1:4001
+     *     _key3.createdIndex=12
+     *     _key3.modifiedIndex=2
+     * </pre>
+     */
+    public Map<String,String> getProperties(String directory, boolean recursive);
+
+}
+-----------------------------------------------
+
+
+=== The EtcdPropertySource
+
+The +EtcdPropertySource+ is automatically registered and requires the etcd servers to be  used to be configured. This
+enables to use e.g. in Docker environments the docker environment configuration mechanisms to configure Tamaya running
+in microservice containers to connect with the according etcd container cluster:
+
+* The property source reads the +tamaya.etcd.server.urls+ system property to evaluate possible etcd servers
+  (comma separated), which can be connected to. On error the API just performs a Round-Robin through the list of
+  configured servers. Without any configuration +http://127.0.0.1:4001+ is used. If no connection to any etcd
+  server can be established a warning will be logged, but deployment will not fail.
+* Additinoally also the
+  accessor allows to configure the socket/connection timeouts by setting +tamaya.etcd.timeout+ in seconds either as
+  system or environment property.
+* The +EtcdPropertySource+ finally also allows the values read from the etcd cluster to be mapped to prefixed
+  context. This can be activated by setting the +-Dtamaya.etcd.prefix=<PREFIX>+ system property. E.g. when the prefix is
+  set to +cluster-config.+ a etcd key of +host:known/all+ is mapped to +cluster-config.host:known/all+.
\ No newline at end of file