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/07 03:00:57 UTC

incubator-tamaya git commit: Added initial implementation of etcd binding.

Repository: incubator-tamaya
Updated Branches:
  refs/heads/master 9283b5751 -> bfb9b0432


Added initial implementation of etcd binding.


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

Branch: refs/heads/master
Commit: bfb9b0432b0fa812906422e790384d7bd4572171
Parents: 9283b57
Author: anatole <an...@apache.org>
Authored: Thu Jan 7 03:00:44 2016 +0100
Committer: anatole <an...@apache.org>
Committed: Thu Jan 7 03:00:44 2016 +0100

----------------------------------------------------------------------
 modules/integration/etcd/pom.xml                | 113 ++++++
 .../org/apache/tamaya/etcd/EtcdAccessor.java    | 403 +++++++++++++++++++
 .../apache/tamaya/etcd/EtcdPropertySource.java  |  91 +++++
 3 files changed, 607 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/bfb9b043/modules/integration/etcd/pom.xml
----------------------------------------------------------------------
diff --git a/modules/integration/etcd/pom.xml b/modules/integration/etcd/pom.xml
new file mode 100644
index 0000000..2c743f1
--- /dev/null
+++ b/modules/integration/etcd/pom.xml
@@ -0,0 +1,113 @@
+<!-- 
+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 current 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.
+-->
+<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.tamaya.ext</groupId>
+        <artifactId>tamaya-integration</artifactId>
+        <version>0.2-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>tamaya-etcd</artifactId>
+    <name>Apache Tamaya Modules Integration - etcd</name>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <camel.version>2.16.0</camel.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>prepare-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.apache.tamaya.integration.camel
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.tamaya</groupId>
+            <artifactId>tamaya-core</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tamaya</groupId>
+            <artifactId>tamaya-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tamaya.ext</groupId>
+            <artifactId>tamaya-functions</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-json_1.0_spec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.johnzon</groupId>
+            <artifactId>johnzon-core</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/bfb9b043/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
new file mode 100644
index 0000000..72745a2
--- /dev/null
+++ b/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java
@@ -0,0 +1,403 @@
+/*
+ * 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.tamaya.etcd;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpStatus;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+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.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Accessor for reading/writing an etcd endpoint.
+ */
+public class EtcdAccessor {
+
+    /** Property that make Johnzon accept commentc. */
+    public static final String JOHNZON_SUPPORTS_COMMENTS_PROP = "org.apache.johnzon.supports-comments";
+    /** The JSON reader factory used. */
+    private JsonReaderFactory readerFactory = initReaderFactory();
+
+    /** Initializes the factory to be used for creating readers. */
+    private JsonReaderFactory initReaderFactory() {
+        Map<String, Object> config = new HashMap<String, Object>();
+        config.put(JOHNZON_SUPPORTS_COMMENTS_PROP, true);
+        return Json.createReaderFactory(config);
+    }
+
+    private String serverURL;
+    private CloseableHttpClient httpclient = HttpClients.createDefault();
+
+    public EtcdAccessor() throws MalformedURLException {
+        this(System.getProperty("etcd.url", "http://127.0.0.1:4001"));
+    }
+
+    public EtcdAccessor(String server) throws MalformedURLException {
+        if(server.endsWith("/")){
+            serverURL = server.substring(0, server.length()-1);
+        } else{
+            serverURL = server;
+        }
+
+    }
+
+    public String getVersion(){
+        CloseableHttpResponse response = null;
+        String version = "<ERROR>";
+        try {
+            CloseableHttpClient httpclient = HttpClients.createDefault();
+            HttpGet httpGet = new HttpGet(serverURL + "/version");
+            response = httpclient.execute(httpGet);
+            HttpEntity entity = null;
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                entity = response.getEntity();
+                // and ensure it is fully consumed
+                version = EntityUtils.toString(entity);
+                EntityUtils.consume(entity);
+            }
+            return version;
+        } catch(Exception e){
+            // TODO log error
+        } finally {
+            if(response!=null){
+                try {
+                    response.close();
+                } catch (IOException e) {
+                    // TODO log error
+                }
+            }
+        }
+        return version;
+    }
+
+    /**
+     * <pre>
+     * {
+         "action": "get",
+         "node": {
+         "createdIndex": 2,
+         "key": "/message",
+         "modifiedIndex": 2,
+         "value": "Hello world"
+         }
+     * }
+     * </pre>
+     * @param key the requested key
+     * @return
+     */
+    public Map<String,String> get(String key){
+        CloseableHttpResponse response = null;
+        Map<String,String> result = new HashMap<>();
+        result.put("_" + key +".source", "[etcd]"+serverURL);
+        try {
+            HttpGet httpGet = new HttpGet(serverURL + "/v2/keys/"+key);
+            response = httpclient.execute(httpGet);
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                HttpEntity entity = response.getEntity();
+                JsonReader reader = readerFactory.createReader(new StringReader(EntityUtils.toString(entity)));
+                JsonObject o = reader.readObject();
+                JsonObject node = o.getJsonObject("node");
+                if(node.containsKey("createdIndex")) {
+                    result.put("_" + key +".createdIndex", String.valueOf(node.getInt("createdIndex")));
+                }
+                if(node.containsKey("modifiedIndex")) {
+                    result.put("_" + key +".modifiedIndex", String.valueOf(node.getInt("modifiedIndex")));
+                }
+                if(node.containsKey("expiration")) {
+                    result.put("_" + key +".expiration", String.valueOf(node.getInt("expiration")));
+                }
+                if(node.containsKey("_" + key +".ttl")) {
+                    result.put("_" + key +".ttl", String.valueOf(node.getInt("ttl")));
+                }
+                result.put(key, o.getString("value"));
+                EntityUtils.consume(entity);
+            }
+        } catch(Exception e){
+            // TODO log error
+            result.put("_" + key +".error", e.toString());
+        } finally {
+            if(response!=null){
+                try {
+                    response.close();
+                } catch (IOException e) {
+                    // TODO log error
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * <pre>
+     *     {
+     "action": "set",
+     "node": {
+     "createdIndex": 3,
+     "key": "/message",
+     "modifiedIndex": 3,
+     "value": "Hello etcd"
+     },
+     "prevNode": {
+     "createdIndex": 2,
+     "key": "/message",
+     "value": "Hello world",
+     "modifiedIndex": 2
+     }
+     }
+     * </pre>
+     * @param key
+     * @param value
+     * @param ttlSeconds
+     * @return
+     */
+    public Map<String,String> set(String key, String value, Integer ttlSeconds){
+        CloseableHttpResponse response = null;
+        Map<String,String> result = new HashMap<>();
+        result.put("_" + key +".source", "[etcd]"+serverURL);
+        try{
+            HttpPut put = new HttpPut(serverURL + "/v2/keys/"+key);
+            List<NameValuePair> nvps = new ArrayList<>();
+            nvps.add(new BasicNameValuePair("value", value));
+            if(ttlSeconds!=null){
+                result.put("ttl", ttlSeconds.toString());
+                nvps.add(new BasicNameValuePair("ttl", ttlSeconds.toString()));
+            }
+            put.setEntity(new UrlEncodedFormEntity(nvps));
+            response = httpclient.execute(put);
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                HttpEntity entity = response.getEntity();
+                JsonReader reader = readerFactory.createReader(new StringReader(EntityUtils.toString(entity)));
+                JsonObject o = reader.readObject();
+                JsonObject node = o.getJsonObject("node");
+                if(node.containsKey("createdIndex")) {
+                    result.put("_" + key +".createdIndex", String.valueOf(node.getInt("createdIndex")));
+                }
+                if(node.containsKey("modifiedIndex")) {
+                    result.put("_" + key +".modifiedIndex", String.valueOf(node.getInt("modifiedIndex")));
+                }
+                if(node.containsKey("expiration")) {
+                    result.put("_" + key +".expiration", String.valueOf(node.getInt("expiration")));
+                }
+                if(node.containsKey("_" + key +".ttl")) {
+                    result.put("_" + key +".ttl", String.valueOf(node.getInt("ttl")));
+                }
+                result.put("value", node.getString("value"));
+                JsonObject prevNode = o.getJsonObject("prevNode");
+                if(prevNode!=null) {
+                    if (prevNode.containsKey("createdIndex")) {
+                        result.put("_" + key +".prevNode.createdIndex", String.valueOf(prevNode.getInt("createdIndex")));
+                    }
+                    if (prevNode.containsKey("modifiedIndex")) {
+                        result.put("_" + key +".prevNode.modifiedIndex", String.valueOf(prevNode.getInt("modifiedIndex")));
+                    }
+                    if(prevNode.containsKey("expiration")) {
+                        result.put("_" + key +".prevNode.expiration", String.valueOf(prevNode.getInt("expiration")));
+                    }
+                    if(prevNode.containsKey("ttl")) {
+                        result.put("_" + key +".prevNode.ttl", String.valueOf(prevNode.getInt("ttl")));
+                    }
+                    result.put("_" + key +".prevNode.value", prevNode.getString("value"));
+                }
+                EntityUtils.consume(entity);
+            }
+        } catch(Exception e){
+            // TODO log error
+            result.put("_" + key +".error", e.toString());
+        } finally {
+            if(response!=null){
+                try {
+                    response.close();
+                } catch (IOException e) {
+                    // TODO log error
+                }
+            }
+        }
+        return result;
+    }
+
+
+    public Map<String,String> delete(String key){
+        CloseableHttpResponse response = null;
+        Map<String,String> result = new HashMap<>();
+        result.put("key", key);
+        result.put("_" + key +".source", "[etcd]"+serverURL);
+        try{
+            HttpDelete delete = new HttpDelete(serverURL + "/v2/keys/"+key);
+            List<NameValuePair> nvps = new ArrayList<>();
+            // delete.setEntity(new UrlEncodedFormEntity(nvps));
+            response = httpclient.execute(delete);
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                HttpEntity entity = response.getEntity();
+                JsonReader reader = readerFactory.createReader(new StringReader(EntityUtils.toString(entity)));
+                JsonObject o = reader.readObject();
+                JsonObject node = o.getJsonObject("node");
+                if(node.containsKey("createdIndex")) {
+                    result.put("_" + key +".createdIndex", String.valueOf(node.getInt("createdIndex")));
+                }
+                if(node.containsKey("modifiedIndex")) {
+                    result.put("_" + key +".modifiedIndex", String.valueOf(node.getInt("modifiedIndex")));
+                }
+                if(node.containsKey("expiration")) {
+                    result.put("_" + key +".expiration", String.valueOf(node.getInt("expiration")));
+                }
+                if(node.containsKey("ttl")) {
+                    result.put("_" + key +".ttl", String.valueOf(node.getInt("ttl")));
+                }
+                JsonObject prevNode = o.getJsonObject("prevNode");
+                if(prevNode!=null) {
+                    if (prevNode.containsKey("createdIndex")) {
+                        result.put("_" + key +".prevNode.createdIndex", String.valueOf(prevNode.getInt("createdIndex")));
+                    }
+                    if (prevNode.containsKey("modifiedIndex")) {
+                        result.put("_" + key +".prevNode.modifiedIndex", String.valueOf(prevNode.getInt("modifiedIndex")));
+                    }
+                    if(prevNode.containsKey("expiration")) {
+                        result.put("_" + key +".prevNode.expiration", String.valueOf(prevNode.getInt("expiration")));
+                    }
+                    if(prevNode.containsKey("ttl")) {
+                        result.put("_" + key +".prevNode.ttl", String.valueOf(prevNode.getInt("ttl")));
+                    }
+                    result.put("_" + key +".prevNode.value", prevNode.getString("value"));
+                }
+                EntityUtils.consume(entity);
+            }
+        } catch(Exception e){
+            // TODO log error
+            result.put("_" + key +".error", e.toString());
+        } finally {
+            if(response!=null){
+                try {
+                    response.close();
+                } catch (IOException e) {
+                    // TODO log error
+                }
+            }
+        }
+        return result;
+    }
+
+    public Map<String,String> getProperties(String directory){
+        return getProperties(directory, true);
+    }
+
+    /*
+    {
+    "action": "get",
+    "node": {
+        "key": "/",
+        "dir": true,
+        "nodes": [
+            {
+                "key": "/foo_dir",
+                "dir": true,
+                "modifiedIndex": 2,
+                "createdIndex": 2
+            },
+            {
+                "key": "/foo",
+                "value": "two",
+                "modifiedIndex": 1,
+                "createdIndex": 1
+            }
+        ]
+    }
+}
+     */
+    public Map<String,String> getProperties(String directory, boolean recursive){
+        CloseableHttpResponse response = null;
+        Map<String,String> result = new HashMap<>();
+        result.put("_" + directory +".source", "[etcd]"+serverURL);
+        try{
+            HttpGet get = new HttpGet(serverURL + "/v2/keys/"+directory+"?recursive="+recursive);
+            response = httpclient.execute(get);
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                result.put("_" + directory +".source", "[etcd]"+serverURL);
+                HttpEntity entity = response.getEntity();
+                JsonReader reader = readerFactory.createReader(new StringReader(EntityUtils.toString(entity)));
+                JsonObject o = reader.readObject();
+                JsonObject node = o.getJsonObject("node");
+                if(node!=null){
+                    addNodes(result, node);
+                }
+                EntityUtils.consume(entity);
+            }
+        } catch(Exception e){
+            // TODO log error
+            result.put("_" + directory +".error", e.toString());
+        } finally {
+            if(response!=null){
+                try {
+                    response.close();
+                } catch (IOException e) {
+                    // TODO log error
+                }
+            }
+        }
+        return result;
+    }
+
+    private void addNodes(Map<String, String> result, JsonObject node) {
+        if(node.getBoolean("dir", false)) {
+            String key = node.getString("key").substring(1);
+            result.put(key, node.getString("value"));
+            if (node.containsKey("createdIndex")) {
+                result.put("_" + key + ".createdIndex", String.valueOf(node.getInt("createdIndex")));
+            }
+            if (node.containsKey("modifiedIndex")) {
+                result.put("_" + key + ".modifiedIndex", String.valueOf(node.getInt("modifiedIndex")));
+            }
+            if (node.containsKey("expiration")) {
+                result.put("_" + key + ".expiration", String.valueOf(node.getInt("expiration")));
+            }
+            if (node.containsKey("ttl")) {
+                result.put("_" + key + ".ttl", String.valueOf(node.getInt("ttl")));
+            }
+            result.put("_" + key +".source", "[etcd]"+serverURL);
+        } else {
+            JsonArray nodes = node.getJsonArray("node");
+            if (nodes != null) {
+                for (int i = 0; i < nodes.size(); i++) {
+                    addNodes(result, nodes.getJsonObject(i));
+                }
+            }
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/bfb9b043/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
new file mode 100644
index 0000000..96ebdf3
--- /dev/null
+++ b/modules/integration/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tamaya.etcd;
+
+import org.apache.tamaya.spi.PropertySource;
+
+import java.net.MalformedURLException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Propertysource that is reading configuration from a configured etcd endpoint.
+ */
+public class EtcdPropertySource implements PropertySource{
+
+    private EtcdAccessor etcdBackend;
+
+    public EtcdPropertySource(){
+        try {
+            etcdBackend = new EtcdAccessor();
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public int getOrdinal() {
+        String configuredOrdinal = get(TAMAYA_ORDINAL);
+        if(configuredOrdinal!=null){
+            try{
+                return Integer.parseInt(configuredOrdinal);
+            } catch(Exception e){
+                Logger.getLogger(getClass().getName()).log(Level.WARNING,
+                        "Configured Ordinal is not an int number: " + configuredOrdinal, e);
+            }
+        }
+        return getDefaultOrdinal();
+    }
+
+    /**
+     * Returns the  default ordinal used, when no ordinal is set, or the ordinal was not parseable to an int value.
+     * @return the  default ordinal used, by default 0.
+     */
+    public int getDefaultOrdinal(){
+        return 100;
+    }
+
+    @Override
+    public String getName() {
+        return "etcd";
+    }
+
+    @Override
+    public String get(String key) {
+        if(etcdBackend!=null) {
+            return etcdBackend.get(key).get(key);
+        }
+        return null;
+    }
+
+    @Override
+    public Map<String, String> getProperties() {
+        if(etcdBackend!=null) {
+            return etcdBackend.getProperties("");
+        }
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public boolean isScannable() {
+        return true;
+    }
+}