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;
+ }
+}