Added missing files, synched Workspace.
Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/commit/358828fe Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/tree/358828fe Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/diff/358828fe Branch: refs/heads/master Commit: 358828fe4b0df808de74cceebe892c79716c171c Parents: 6fa2b34 Author: anatole <[email protected]> Authored: Wed Nov 2 00:46:53 2016 +0100 Committer: anatole <[email protected]> Committed: Wed Nov 2 00:46:53 2016 +0100 ---------------------------------------------------------------------- camel/pom.xml | 101 ++++ .../camel/TamayaPropertiesComponent.java | 78 +++ .../camel/TamayaPropertyResolver.java | 53 ++ .../camel/TamayaPropertyResolverTest.java | 116 +++++ .../test/resources/META-INF/camelcontext.xml | 52 ++ .../META-INF/javaconfiguration.properties | 19 + camel/src/test/resources/META-INF/routes.xml | 39 ++ consul/pom.xml | 99 ++++ .../apache/tamaya/consul/ConsulBackends.java | 59 +++ .../tamaya/consul/ConsulPropertySource.java | 198 +++++++ .../org.apache.tamaya.spi.PropertySource | 19 + etcd/pom.xml | 97 ++++ .../org/apache/tamaya/etcd/EtcdAccessor.java | 520 +++++++++++++++++++ .../org/apache/tamaya/etcd/EtcdBackends.java | 65 +++ .../apache/tamaya/etcd/EtcdPropertySource.java | 209 ++++++++ .../org.apache.tamaya.spi.PropertySource | 19 + .../apache/tamaya/etcd/EtcdAccessorTest.java | 116 +++++ .../tamaya/etcd/EtcdPropertySourceTest.java | 74 +++ .../tamaya/jodatime/DurationConverter.java | 72 +++ .../tamaya/jodatime/DurationConverterIT.java | 51 ++ management/pom.xml | 81 +++ .../management/ConfigManagementSupport.java | 128 +++++ .../apache/tamaya/management/ManagedConfig.java | 110 ++++ .../tamaya/management/ManagedConfigMBean.java | 119 +++++ .../src/main/resources/META-INF/beans.xml | 24 + .../META-INF/javaconfiguration.properties | 19 + ....apache.tamaya.management.ManagedConfigMBean | 19 + .../management/internal/ManagedConfigTest.java | 118 +++++ .../src/test/resources/META-INF/beans.xml | 24 + metamodel/pom.xml | 123 +++++ .../metamodel/ConfigurationContextBuilder.java | 354 +++++++++++++ .../org/apache/tamaya/metamodel/Context.java | 130 +++++ ...efaultRefreshablePropertySourceProvider.java | 72 +++ .../metamodel/internal/FactoryManager.java | 140 +++++ .../tamaya/metamodel/internal/Refreshable.java | 37 ++ .../tamaya/metamodel/internal/SourceConfig.java | 233 +++++++++ .../apache/tamaya/metamodel/package-info.java | 22 + .../metamodel/spi/PropertySourceFactory.java | 49 ++ .../spi/PropertySourceProviderFactory.java | 50 ++ metamodel/src/test/resources/tamaya-config.json | 39 ++ metamodel/src/test/resources/tamaya-config.xml | 62 +++ metamodel/src/test/resources/tamaya-config.yaml | 87 ++++ osgi/pom.xml | 103 ++++ .../tamaya/integration/osgi/Activator.java | 134 +++++ .../integration/osgi/OSGIConfigRootMapper.java | 36 ++ .../osgi/OSGIEnhancedConfiguration.java | 117 +++++ .../integration/osgi/TamayaConfigAdminImpl.java | 196 +++++++ .../osgi/TamayaConfigurationImpl.java | 127 +++++ .../META-INF/javaconfiguration.properties | 18 + osgi/src/test/resources/arquillian.xml | 27 + osgi/src/test/resources/felix.properties | 23 + ...MetainfConfigPropertySourceProviderTest.java | 37 ++ server/pom.xml | 203 ++++++++ .../apache/tamaya/server/ConfigServiceApp.java | 77 +++ .../tamaya/server/ConfigurationResource.java | 310 +++++++++++ .../apache/tamaya/server/VersionProperties.java | 68 +++ .../apache/tamaya/server/spi/ScopeManager.java | 84 +++ .../apache/tamaya/server/spi/ScopeProvider.java | 40 ++ .../META-INF/tamaya-server-version.properties | 22 + server/src/main/resources/banner.txt | 14 + server/src/main/resources/config-server.yml | 31 ++ .../tamaya/server/ConfigServiceAppTest.java | 32 ++ .../org/apache/tamaya/server/EtcdAccessor.java | 519 ++++++++++++++++++ .../tamaya/server/VersionPropertiesTest.java | 46 ++ ui/base/src/main/main5.iml | 11 + ui/events/pom.xml | 36 ++ ui/events/src/main/main7.iml | 12 + ui/mutableconfig/src/main/main8.iml | 12 + ui/pom.xml | 46 ++ validation/pom.xml | 112 ++++ .../resources/META-INF/configmodel.properties | 35 ++ ...org.apache.tamaya.events.ConfigEventListener | 19 + .../java/test/model/TestConfigAccessor.java | 45 ++ .../resources/META-INF/configmodel.properties | 96 ++++ .../META-INF/javaconfiguration.properties | 22 + ...org.apache.tamaya.model.spi.ModelProviderSpi | 19 + .../src/test/resources/examples/configmodel.ini | 76 +++ .../test/resources/examples/configmodel.json | 108 ++++ .../resources/examples/configmodel.properties | 96 ++++ .../src/test/resources/examples/configmodel.xml | 97 ++++ .../test/resources/examples/configmodel.yaml | 106 ++++ 81 files changed, 7308 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/camel/pom.xml ---------------------------------------------------------------------- diff --git a/camel/pom.xml b/camel/pom.xml new file mode 100644 index 0000000..d07de87 --- /dev/null +++ b/camel/pom.xml @@ -0,0 +1,101 @@ +<!-- +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-sandbox</artifactId> + <version>0.3-incubating-SNAPSHOT</version> + <relativePath>..</relativePath> + </parent> + + <artifactId>tamaya-camel</artifactId> + <name>Apache Tamaya Modules - Apache Camel Support</name> + <packaging>bundle</packaging> + + <properties> + <camel.version>2.17.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>java-hamcrest</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.camel</groupId> + <artifactId>camel-core</artifactId> + <version>${camel.version}</version> + <scope>provided</scope> + </dependency> + </dependencies> + +</project> http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/camel/src/main/java/org/apache/tamaya/integration/camel/TamayaPropertiesComponent.java ---------------------------------------------------------------------- diff --git a/camel/src/main/java/org/apache/tamaya/integration/camel/TamayaPropertiesComponent.java b/camel/src/main/java/org/apache/tamaya/integration/camel/TamayaPropertiesComponent.java new file mode 100644 index 0000000..8b776a5 --- /dev/null +++ b/camel/src/main/java/org/apache/tamaya/integration/camel/TamayaPropertiesComponent.java @@ -0,0 +1,78 @@ +/* + * 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.integration.camel; + +import java.util.Properties; + +import org.apache.camel.component.properties.PropertiesComponent; +import org.apache.tamaya.ConfigurationProvider; + +/** + * Default Camel PropertiesComponent that additionally has cfg and tamaya prefixes configured for resolution of + * entries from tamaya. + */ +public class TamayaPropertiesComponent extends PropertiesComponent{ + + /** + * Constructor similar to parent. + */ + public TamayaPropertiesComponent(){ + super(); + addFunction(new TamayaPropertyResolver("tamaya")); + addFunction(new TamayaPropertyResolver("cfg")); + setTamayaOverrides(true); + } + + /** + * Constructor similar to parent with additional locations. + * @param locations additional locations for Camel. + */ + public TamayaPropertiesComponent(String ... locations){ + super(locations); + addFunction(new TamayaPropertyResolver("tamaya")); + addFunction(new TamayaPropertyResolver("cfg")); + setTamayaOverrides(true); + } + + /** + * Constructor similar to parent with only one location. + * @param location addition location for Camel. + */ + public TamayaPropertiesComponent(String location){ + super(location); + addFunction(new TamayaPropertyResolver("tamaya")); + addFunction(new TamayaPropertyResolver("cfg")); + setTamayaOverrides(true); + } + + /** + * Apply the current Tamaya properties (configuration) as override properties evaluated first by camel before + * evaluating other uris. + * @param enabled flag to define if tamaya values override everything else. + */ + public void setTamayaOverrides(boolean enabled){ + if(enabled){ + final Properties props = new Properties(); + props.putAll(ConfigurationProvider.getConfiguration().getProperties()); + setOverrideProperties(props); + } else{ + setOverrideProperties(null); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/camel/src/main/java/org/apache/tamaya/integration/camel/TamayaPropertyResolver.java ---------------------------------------------------------------------- diff --git a/camel/src/main/java/org/apache/tamaya/integration/camel/TamayaPropertyResolver.java b/camel/src/main/java/org/apache/tamaya/integration/camel/TamayaPropertyResolver.java new file mode 100644 index 0000000..6b6ada9 --- /dev/null +++ b/camel/src/main/java/org/apache/tamaya/integration/camel/TamayaPropertyResolver.java @@ -0,0 +1,53 @@ +/* + * 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.integration.camel; + +import org.apache.camel.component.properties.PropertiesFunction; +import org.apache.tamaya.Configuration; +import org.apache.tamaya.ConfigurationProvider; + +import java.util.Objects; + + +/** + * Implementation of the Camel Properties SPI using Tamaya configuration. + */ +public class TamayaPropertyResolver implements PropertiesFunction{ + + private final String prefix; + + /** + * Creates a new instance. + * @param configPrefix the prefix to be registered for explicit resolution by this resolver function, not null. + */ + public TamayaPropertyResolver(String configPrefix){ + this.prefix = Objects.requireNonNull(configPrefix); + } + + @Override + public String getName() { + return prefix; + } + + @Override + public String apply(String remainder) { + Configuration config = ConfigurationProvider.getConfiguration(); + return config.get(remainder); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/camel/src/test/java/org/apache/tamaya/integration/camel/TamayaPropertyResolverTest.java ---------------------------------------------------------------------- diff --git a/camel/src/test/java/org/apache/tamaya/integration/camel/TamayaPropertyResolverTest.java b/camel/src/test/java/org/apache/tamaya/integration/camel/TamayaPropertyResolverTest.java new file mode 100644 index 0000000..0cba47e --- /dev/null +++ b/camel/src/test/java/org/apache/tamaya/integration/camel/TamayaPropertyResolverTest.java @@ -0,0 +1,116 @@ +/* + * 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.integration.camel; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.ProxyBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.model.RouteDefinition; +import org.apache.camel.model.RoutesDefinition; +import org.junit.Test; + +import java.io.InputStream; + +import static org.junit.Assert.*; + +/** + * Tests for integration of Tamaya with Apache Camel using Java DSL and XML DSL. + */ +public class TamayaPropertyResolverTest { + + @Test + public void testJavaDSLWithCfgResolution() throws Exception { + CamelContext camelContext = new DefaultCamelContext(); + camelContext.addComponent("properties", new TamayaPropertiesComponent()); + RouteBuilder builder = new RouteBuilder() { + public void configure() { + from("direct:hello").transform().simple("{{cfg:message}}"); + } + }; + camelContext.addRoutes(builder); + camelContext.start(); + // test configuration is injected right... + Greeter proxy = new ProxyBuilder(camelContext).endpoint("direct:hello").build(Greeter.class); + String greetMessage = proxy.greet(); + assertEquals("Good Bye from Apache Tamaya!", greetMessage); + } + + @Test + public void testJavaDSLWithTamayaResolution() throws Exception { + CamelContext camelContext = new DefaultCamelContext(); + camelContext.addComponent("properties", new TamayaPropertiesComponent()); + RouteBuilder builder = new RouteBuilder() { + public void configure() { + from("direct:hello").transform().simple("{{tamaya:message}}"); + } + }; + camelContext.addRoutes(builder); + camelContext.start(); + // test configuration is injected right... + Greeter proxy = new ProxyBuilder(camelContext).endpoint("direct:hello").build(Greeter.class); + String greetMessage = proxy.greet(); + assertEquals("Good Bye from Apache Tamaya!", greetMessage); + } + + @Test + public void testJavaDSLWithOverrideActive() throws Exception { + CamelContext camelContext = new DefaultCamelContext(); + TamayaPropertiesComponent props = new TamayaPropertiesComponent(); + props.setTamayaOverrides(true); + camelContext.addComponent("properties", props); + RouteBuilder builder = new RouteBuilder() { + public void configure() { + from("direct:hello").transform().simple("{{message}}"); + } + }; + camelContext.addRoutes(builder); + camelContext.start(); + // test configuration is injected right... + Greeter proxy = new ProxyBuilder(camelContext).endpoint("direct:hello").build(Greeter.class); + String greetMessage = proxy.greet(); + assertEquals("Good Bye from Apache Tamaya!", greetMessage); + } + + @Test + public void testXmlDSL() throws Exception { + CamelContext camelContext = new DefaultCamelContext(); + // This is normally done by the Spring implemented registry, we keep it simple here... + TamayaPropertiesComponent props = new TamayaPropertiesComponent(); + props.setTamayaOverrides(true); + camelContext.addComponent("properties", props); + // Read routes from XML DSL + InputStream is = getClass().getResourceAsStream("/META-INF/routes.xml"); + RoutesDefinition routes = camelContext.loadRoutesDefinition(is); + for(RouteDefinition def: routes.getRoutes()) { + camelContext.addRouteDefinition(def); + } + camelContext.start(); + Greeter greeter = new ProxyBuilder(camelContext).endpoint("direct:hello1").build(Greeter.class); + assertEquals("Good Bye from Apache Tamaya!", greeter.greet()); + greeter = new ProxyBuilder(camelContext).endpoint("direct:hello2").build(Greeter.class); + assertEquals("Good Bye from Apache Tamaya!", greeter.greet()); + greeter = new ProxyBuilder(camelContext).endpoint("direct:hello3").build(Greeter.class); + assertEquals("Good Bye from Apache Tamaya!", greeter.greet()); + } + + public interface Greeter { + String greet(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/camel/src/test/resources/META-INF/camelcontext.xml ---------------------------------------------------------------------- diff --git a/camel/src/test/resources/META-INF/camelcontext.xml b/camel/src/test/resources/META-INF/camelcontext.xml new file mode 100644 index 0000000..6b99d3d --- /dev/null +++ b/camel/src/test/resources/META-INF/camelcontext.xml @@ -0,0 +1,52 @@ +<!-- +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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd + "> + + <!-- this is an included XML file where we only the the routeContext --> + <routeContext id="myCoolRoutes" xmlns="http://camel.apache.org/schema/spring"> + <route id="r1"> + <from uri="direct:hello1"/> + <transform> + <simple>{{message}}</simple> + </transform> + </route> + <route id="r2"> + <from uri="direct:hello2"/> + <transform> + <simple>{{cfg:message}}</simple> + </transform> + </route> + <route id="r3"> + <from uri="direct:hello3"/> + <transform> + <simple>{{tamaya:message}}</simple> + </transform> + </route> + </routeContext> + + <bean id="properties" class="org.apache.tamaya.integration.camel.TamayaPropertiesComponent"> + <property name="tamayaOverrides" value="true"/> + </bean> + +</beans> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/camel/src/test/resources/META-INF/javaconfiguration.properties ---------------------------------------------------------------------- diff --git a/camel/src/test/resources/META-INF/javaconfiguration.properties b/camel/src/test/resources/META-INF/javaconfiguration.properties new file mode 100644 index 0000000..fbe9178 --- /dev/null +++ b/camel/src/test/resources/META-INF/javaconfiguration.properties @@ -0,0 +1,19 @@ +# +# 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. +# +message=Good Bye from Apache Tamaya! \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/camel/src/test/resources/META-INF/routes.xml ---------------------------------------------------------------------- diff --git a/camel/src/test/resources/META-INF/routes.xml b/camel/src/test/resources/META-INF/routes.xml new file mode 100644 index 0000000..5ec3529 --- /dev/null +++ b/camel/src/test/resources/META-INF/routes.xml @@ -0,0 +1,39 @@ +<!-- +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. +--> +<routes xmlns="http://camel.apache.org/schema/spring"> + <description>Routes for testing.</description> + <route> + <from uri="direct:hello1"/> + <transform> + <simple>{{message}}</simple> + </transform> + </route> + <route> + <from uri="direct:hello2"/> + <transform> + <simple>{{cfg:message}}</simple> + </transform> + </route> + <route> + <from uri="direct:hello3"/> + <transform> + <simple>{{tamaya:message}}</simple> + </transform> + </route> +</routes> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/consul/pom.xml ---------------------------------------------------------------------- diff --git a/consul/pom.xml b/consul/pom.xml new file mode 100644 index 0000000..f3d8611 --- /dev/null +++ b/consul/pom.xml @@ -0,0 +1,99 @@ +<!-- +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-sandbox</artifactId> + <version>0.3-incubating-SNAPSHOT</version> + <relativePath>..</relativePath> + </parent> + + <artifactId>tamaya-consul</artifactId> + <name>Apache Tamaya Modules - Consul</name> + <packaging>bundle</packaging> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Export-Package> + org.apache.tamaya.consul + </Export-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>java-hamcrest</artifactId> + </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.tamaya.ext</groupId> + <artifactId>tamaya-mutable-config</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>com.orbitz.consul</groupId> + <artifactId>consul-client</artifactId> + <version>0.9.16</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-rs-client</artifactId> + <version>3.0.3</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-transports-http-hc</artifactId> + <version>3.0.3</version> + </dependency> + </dependencies> + +</project> http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/consul/src/main/java/org/apache/tamaya/consul/ConsulBackends.java ---------------------------------------------------------------------- diff --git a/consul/src/main/java/org/apache/tamaya/consul/ConsulBackends.java b/consul/src/main/java/org/apache/tamaya/consul/ConsulBackends.java new file mode 100644 index 0000000..4eab141 --- /dev/null +++ b/consul/src/main/java/org/apache/tamaya/consul/ConsulBackends.java @@ -0,0 +1,59 @@ +/* + * 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.consul; + +import com.google.common.net.HostAndPort; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Singleton that reads and stores the current consul setup, especially the possible host:ports to be used. + */ +public final class ConsulBackends { + + private static final Logger LOG = Logger.getLogger(ConsulBackends.class.getName()); + private static List<HostAndPort> consulBackends = new ArrayList<>(); + + static{ + String serverURLs = System.getProperty("tamaya.consul.urls"); + if(serverURLs==null){ + serverURLs = System.getenv("tamaya.consul.urls"); + } + if(serverURLs==null){ + serverURLs = "127.0.0.1:8300"; + } + for(String url:serverURLs.split("\\,")) { + try{ + consulBackends.add(HostAndPort.fromString(url.trim())); + LOG.info("Using consul endoint: " + url); + } catch(Exception e){ + LOG.log(Level.SEVERE, "Error initializing consul accessor for URL: " + url, e); + } + } + } + + private ConsulBackends(){} + + public static List<HostAndPort> getConsulBackends(){ + return consulBackends; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/consul/src/main/java/org/apache/tamaya/consul/ConsulPropertySource.java ---------------------------------------------------------------------- diff --git a/consul/src/main/java/org/apache/tamaya/consul/ConsulPropertySource.java b/consul/src/main/java/org/apache/tamaya/consul/ConsulPropertySource.java new file mode 100644 index 0000000..1936362 --- /dev/null +++ b/consul/src/main/java/org/apache/tamaya/consul/ConsulPropertySource.java @@ -0,0 +1,198 @@ +/* + * 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.consul; + +import com.google.common.base.Optional; +import com.google.common.net.HostAndPort; +import com.orbitz.consul.Consul; +import com.orbitz.consul.KeyValueClient; +import com.orbitz.consul.model.kv.Value; +import org.apache.tamaya.mutableconfig.spi.ConfigChangeRequest; +import org.apache.tamaya.mutableconfig.spi.MutablePropertySource; +import org.apache.tamaya.spi.PropertyValue; +import org.apache.tamaya.spi.PropertyValueBuilder; +import org.apache.tamaya.spisupport.BasePropertySource; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Propertysource that is reading configuration from a configured consul endpoint. Setting + * {@code consul.prefix} as system property maps the consul based onfiguration + * to this prefix namespace. Consul servers are configured as {@code consul.urls} system or environment property. + */ +public class ConsulPropertySource extends BasePropertySource +implements MutablePropertySource{ + private static final Logger LOG = Logger.getLogger(ConsulPropertySource.class.getName()); + + private String prefix = System.getProperty("tamaya.consul.prefix", ""); + + + @Override + public int getOrdinal() { + PropertyValue configuredOrdinal = get(TAMAYA_ORDINAL); + if(configuredOrdinal!=null){ + try{ + return Integer.parseInt(configuredOrdinal.getValue()); + } 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 1000. + */ + public int getDefaultOrdinal(){ + return 1000; + } + + @Override + public String getName() { + return "consul"; + } + + @Override + public PropertyValue get(String key) { + // check prefix, if key does not start with it, it is not part of our name space + // if so, the prefix part must be removedProperties, so etcd can resolve without it + if(!key.startsWith(prefix)){ + return null; + } else{ + key = key.substring(prefix.length()); + } + 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(HostAndPort hostAndPort: ConsulBackends.getConsulBackends()){ + try{ + Consul consul = Consul.builder().withHostAndPort(hostAndPort).build(); + KeyValueClient kvClient = consul.keyValueClient(); + Optional<Value> valueOpt = kvClient.getValue(reqKey); + if(!valueOpt.isPresent()) { + LOG.log(Level.FINE, "key not found in consul: " + reqKey); + }else{ + // No repfix mapping necessary here, since we only access/return the value... + Value value = valueOpt.get(); + Map<String,String> props = new HashMap<>(); + props.put(reqKey+".createIndex", String.valueOf(value.getCreateIndex())); + props.put(reqKey+".modifyIndex", String.valueOf(value.getModifyIndex())); + props.put(reqKey+".lockIndex", String.valueOf(value.getLockIndex())); + props.put(reqKey+".flags", String.valueOf(value.getFlags())); + return new PropertyValueBuilder(key, value.getValue().get(), getName()).setContextData(props).build(); + } + } catch(Exception e){ + LOG.log(Level.FINE, "etcd access failed on " + hostAndPort + ", trying next...", e); + } + } + return null; + } + + @Override + public Map<String, String> getProperties() { +// for(HostAndPort hostAndPort: ConsulBackends.getConsulBackends()){ +// try{ +// Consul consul = Consul.builder().withHostAndPort(hostAndPort).build(); +// KeyValueClient kvClient = consul.keyValueClient(); +// Optional<Value> valueOpt = kvClient.getValue(reqKey); +// try{ +// Map<String, String> props = kvClient.getProperties(""); +// if(!props.containsKey("_ERROR")) { +// return mapPrefix(props); +// } else{ +// LOG.log(Level.FINE, "consul error on " + hostAndPort + ": " + props.get("_ERROR")); +// } +// } catch(Exception e){ +// LOG.log(Level.FINE, "consul access failed on " + hostAndPort + ", trying next...", e); +// } +// } catch(Exception e){ +// LOG.log(Level.FINE, "etcd access failed on " + hostAndPort + ", 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 false; + } + + @Override + public void applyChange(ConfigChangeRequest configChange) { + for(HostAndPort hostAndPort: ConsulBackends.getConsulBackends()){ + try{ + Consul consul = Consul.builder().withHostAndPort(hostAndPort).build(); + KeyValueClient kvClient = consul.keyValueClient(); + + for(String k: configChange.getRemovedProperties()){ + try{ + kvClient.deleteKey(k); + } catch(Exception e){ + LOG.info("Failed to remove key from consul: " + k); + } + } + for(Map.Entry<String,String> en:configChange.getAddedProperties().entrySet()){ + String key = en.getKey(); + try{ + kvClient.putValue(key,en.getValue()); + }catch(Exception e) { + LOG.info("Failed to add key to consul: " + en.getKey() + "=" + en.getValue()); + } + } + // success: stop here + break; + } catch(Exception e){ + LOG.log(Level.FINE, "consul access failed on " + hostAndPort + ", trying next...", e); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/consul/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertySource ---------------------------------------------------------------------- diff --git a/consul/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertySource b/consul/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertySource new file mode 100644 index 0000000..4996059 --- /dev/null +++ b/consul/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertySource @@ -0,0 +1,19 @@ +# +# 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. +# +org.apache.tamaya.consul.ConsulPropertySource \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/etcd/pom.xml ---------------------------------------------------------------------- diff --git a/etcd/pom.xml b/etcd/pom.xml new file mode 100644 index 0000000..c687b6a --- /dev/null +++ b/etcd/pom.xml @@ -0,0 +1,97 @@ +<!-- +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-sandbox</artifactId> + <version>0.3-incubating-SNAPSHOT</version> + <relativePath>..</relativePath> + </parent> + + <artifactId>tamaya-etcd</artifactId> + <name>Apache Tamaya Modules - etcd PropertySource</name> + <packaging>bundle</packaging> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Export-Package> + org.apache.tamaya.etcd + </Export-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>java-hamcrest</artifactId> + </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-osgi</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> + <dependency> + <groupId>org.apache.tamaya.ext</groupId> + <artifactId>tamaya-mutable-config</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + <optional>true</optional> + </dependency> + </dependencies> + +</project> http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java ---------------------------------------------------------------------- diff --git a/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java b/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java new file mode 100644 index 0000000..4feccfa --- /dev/null +++ b/etcd/src/main/java/org/apache/tamaya/etcd/EtcdAccessor.java @@ -0,0 +1,520 @@ +/* + * 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 java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonReaderFactory; + +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; +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; + +/** + * Accessor for reading to or writing from an etcd endpoint. + */ +public class EtcdAccessor { + + private static final Logger LOG = Logger.getLogger(EtcdAccessor.class.getName()); + + /** + * Timeout in seconds. + */ + private int timeout = 2; + /** + * Timeout in seconds. + */ + private final int socketTimeout = 1000; + /** + * Timeout in seconds. + */ + private final int connectTimeout = 1000; + + /** + * 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 final JsonReaderFactory readerFactory = initReaderFactory(); + + /** + * Initializes the factory to be used for creating readers. + */ + private JsonReaderFactory initReaderFactory() { + final Map<String, Object> config = new HashMap<>(); + config.put(JOHNZON_SUPPORTS_COMMENTS_PROP, true); + return Json.createReaderFactory(config); + } + + /** + * The base server url. + */ + private final String serverURL; + /** + * The http client. + */ + private final CloseableHttpClient httpclient = HttpClients.createDefault(); + + /** + * Creates a new instance with the basic access url. + * + * @param server server url, e.g. {@code http://127.0.0.1:4001}, not null. + */ + public EtcdAccessor(String server) { + this(server, 2); + } + + public EtcdAccessor(String server, int timeout) { + this.timeout = timeout; + if (server.endsWith("/")) { + serverURL = server.substring(0, server.length() - 1); + } else { + serverURL = server; + } + + } + + /** + * Get the etcd server version. + * + * @return the etcd server version, never null. + */ + public String getVersion() { + String version = "<ERROR>"; + try { + final CloseableHttpClient httpclient = HttpClients.createDefault(); + final HttpGet httpGet = new HttpGet(serverURL + "/version"); + httpGet.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(socketTimeout) + .setConnectTimeout(timeout).build()); + try (CloseableHttpResponse response = httpclient.execute(httpGet)) { + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + final HttpEntity entity = response.getEntity(); + // and ensure it is fully consumed + version = EntityUtils.toString(entity); + EntityUtils.consume(entity); + } + } + return version; + } catch (final Exception e) { + LOG.log(Level.INFO, "Error getting etcd version from: " + serverURL, e); + } + return version; + } + + /** + * Ask etcd for a single key, value pair. Hereby the response returned from + * etcd: + * + * <pre> + * { + * "action": "get", + * "node": { + * "createdIndex": 2, + * "key": "/message", + * "modifiedIndex": 2, + * "value": "Hello world" + * } + * } + * </pre> + * + * is mapped to: + * + * <pre> + * key=value + * _key.source=[etcd]http://127.0.0.1:4001 + * _key.createdIndex=12 + * _key.modifiedIndex=34 + * _key.ttl=300 + * _key.expiration=... + * </pre> + * + * @param key the requested key + * @return the mapped result, including meta-entries. + */ + public Map<String, String> get(String key) { + final Map<String, String> result = new HashMap<>(); + try { + final HttpGet httpGet = new HttpGet(serverURL + "/v2/keys/" + key); + httpGet.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(socketTimeout) + .setConnectionRequestTimeout(timeout).setConnectTimeout(connectTimeout).build()); + try (CloseableHttpResponse response = httpclient.execute(httpGet)) { + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + final HttpEntity entity = response.getEntity(); + final JsonReader reader = readerFactory + .createReader(new StringReader(EntityUtils.toString(entity))); + final JsonObject o = reader.readObject(); + final JsonObject node = o.getJsonObject("node"); + if (node.containsKey("value")) { + result.put(key, node.getString("value")); + result.put("_" + key + ".source", "[etcd]" + serverURL); + } + 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.getString("expiration"))); + } + if (node.containsKey("ttl")) { + result.put("_" + key + ".ttl", String.valueOf(node.getInt("ttl"))); + } + EntityUtils.consume(entity); + } else { + result.put("_" + key + ".NOT_FOUND.target", "[etcd]" + serverURL); + } + } + } catch (final Exception 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()); + } + return result; + } + + /** + * Creates/updates an entry in etcd without any ttl set. + * + * @param key the property key, not null + * @param value the value to be set + * @return the result map as described above. + * @see #set(String, String, Integer) + */ + public Map<String, String> set(String key, String value) { + return set(key, value, null); + } + + /** + * Creates/updates an entry in etcd. The response as follows: + * + * <pre> + * { + * "action": "set", + * "node": { + * "createdIndex": 3, + * "key": "/message", + * "modifiedIndex": 3, + * "value": "Hello etcd" + * }, + * "prevNode": { + * "createdIndex": 2, + * "key": "/message", + * "value": "Hello world", + * "modifiedIndex": 2 + * } + * } + * </pre> + * + * is mapped to: + * + * <pre> + * key=value + * _key.source=[etcd]http://127.0.0.1:4001 + * _key.createdIndex=12 + * _key.modifiedIndex=34 + * _key.ttl=300 + * _key.expiry=... + * // optional + * _key.prevNode.createdIndex=12 + * _key.prevNode.modifiedIndex=34 + * _key.prevNode.ttl=300 + * _key.prevNode.expiration=... + * </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) { + final Map<String, String> result = new HashMap<>(); + try { + final HttpPut put = new HttpPut(serverURL + "/v2/keys/" + key); + put.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(socketTimeout) + .setConnectionRequestTimeout(timeout).setConnectTimeout(connectTimeout).build()); + final List<NameValuePair> nvps = new ArrayList<>(); + nvps.add(new BasicNameValuePair("value", value)); + if (ttlSeconds != null) { + nvps.add(new BasicNameValuePair("ttl", ttlSeconds.toString())); + } + put.setEntity(new UrlEncodedFormEntity(nvps)); + try (CloseableHttpResponse response = httpclient.execute(put)) { + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED + || response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + final HttpEntity entity = response.getEntity(); + final JsonReader reader = readerFactory + .createReader(new StringReader(EntityUtils.toString(entity))); + final JsonObject o = reader.readObject(); + final 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.getString("expiration"))); + } + if (node.containsKey("ttl")) { + result.put("_" + key + ".ttl", String.valueOf(node.getInt("ttl"))); + } + result.put(key, node.getString("value")); + result.put("_" + key + ".source", "[etcd]" + serverURL); + parsePrevNode(key, result, node); + EntityUtils.consume(entity); + } + } + } catch (final Exception e) { + LOG.log(Level.INFO, "Error writing to etcd: " + serverURL, e); + result.put("_ERROR", "Error writing '" + key + "' to etcd: " + serverURL + ": " + e.toString()); + } + return result; + } + + /** + * 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 + * _key.expiry=... + * // optional + * _key.prevNode.createdIndex=12 + * _key.prevNode.modifiedIndex=34 + * _key.prevNode.ttl=300 + * _key.prevNode.expiration=... + * _key.prevNode.value=... + * </pre> + * + * @param key the key to be deleted. + * @return the response mpas as described above. + */ + public Map<String, String> delete(String key) { + final Map<String, String> result = new HashMap<>(); + try { + final HttpDelete delete = new HttpDelete(serverURL + "/v2/keys/" + key); + delete.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(socketTimeout) + .setConnectionRequestTimeout(timeout).setConnectTimeout(connectTimeout).build()); + try (CloseableHttpResponse response = httpclient.execute(delete)) { + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + final HttpEntity entity = response.getEntity(); + final JsonReader reader = readerFactory + .createReader(new StringReader(EntityUtils.toString(entity))); + final JsonObject o = reader.readObject(); + final 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.getString("expiration"))); + } + if (node.containsKey("ttl")) { + result.put("_" + key + ".ttl", String.valueOf(node.getInt("ttl"))); + } + parsePrevNode(key, result, o); + EntityUtils.consume(entity); + } + } + } catch (final Exception e) { + LOG.log(Level.INFO, "Error deleting key '" + key + "' from etcd: " + serverURL, e); + result.put("_ERROR", "Error deleting '" + key + "' from etcd: " + serverURL + ": " + e.toString()); + } + return result; + } + + private static void parsePrevNode(String key, Map<String, String> result, JsonObject o) { + if (o.containsKey("prevNode")) { + final JsonObject prevNode = o.getJsonObject("prevNode"); + 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.getString("expiration"))); + } + if (prevNode.containsKey("ttl")) { + result.put("_" + key + ".prevNode.ttl", String.valueOf(prevNode.getInt("ttl"))); + } + result.put("_" + key + ".prevNode.value", prevNode.getString("value")); + } + } + + /** + * Get all properties for the given directory key recursively. + * + * @param directory the directory entry + * @return the properties and its metadata + * @see #getProperties(String, boolean) + */ + public Map<String, String> getProperties(String directory) { + return getProperties(directory, true); + } + + /** + * Access all properties. The response of: + * + * <pre> + * { + * "action": "get", + * "node": { + * "key": "/", + * "dir": true, + * "nodes": [ + * { + * "key": "/foo_dir", + * "dir": true, + * "modifiedIndex": 2, + * "createdIndex": 2 + * }, + * { + * "key": "/foo", + * "value": "two", + * "modifiedIndex": 1, + * "createdIndex": 1 + * } + * ] + * } + * } + * </pre> + * + * is mapped to a regular Tamaya properties map as follows: + * + * <pre> + * key1=myvalue + * _key1.source=[etcd]http://127.0.0.1:4001 + * _key1.createdIndex=12 + * _key1.modifiedIndex=34 + * _key1.ttl=300 + * _key1.expiration=... + * + * 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> + * + * @param directory remote directory to query. + * @param recursive allows to set if querying is performed recursively + * @return all properties read from the remote server. + */ + public Map<String, String> getProperties(String directory, boolean recursive) { + final Map<String, String> result = new HashMap<>(); + try { + final HttpGet get = new HttpGet(serverURL + "/v2/keys/" + directory + "?recursive=" + recursive); + get.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setSocketTimeout(socketTimeout) + .setConnectionRequestTimeout(timeout).setConnectTimeout(connectTimeout).build()); + try (CloseableHttpResponse response = httpclient.execute(get)) { + + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + final HttpEntity entity = response.getEntity(); + final JsonReader reader = readerFactory.createReader(new StringReader(EntityUtils.toString(entity))); + final JsonObject o = reader.readObject(); + final JsonObject node = o.getJsonObject("node"); + if (node != null) { + addNodes(result, node); + } + EntityUtils.consume(entity); + } + } + } catch (final Exception 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()); + } + return result; + } + + /** + * Recursively read out all key/values from this etcd JSON array. + * + * @param result map with key, values and metadata. + * @param node the node to parse. + */ + private void addNodes(Map<String, String> result, JsonObject node) { + if (!node.containsKey("dir") || "false".equals(node.get("dir").toString())) { + final 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.getString("expiration"))); + } + if (node.containsKey("ttl")) { + result.put("_" + key + ".ttl", String.valueOf(node.getInt("ttl"))); + } + result.put("_" + key + ".source", "[etcd]" + serverURL); + } else { + final JsonArray nodes = node.getJsonArray("nodes"); + if (nodes != null) { + for (int i = 0; i < nodes.size(); i++) { + addNodes(result, nodes.getJsonObject(i)); + } + } + } + } + + /** + * Access the server root URL used by this accessor. + * + * @return the server root URL. + */ + public String getUrl() { + return serverURL; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/etcd/src/main/java/org/apache/tamaya/etcd/EtcdBackends.java ---------------------------------------------------------------------- diff --git a/etcd/src/main/java/org/apache/tamaya/etcd/EtcdBackends.java b/etcd/src/main/java/org/apache/tamaya/etcd/EtcdBackends.java new file mode 100644 index 0000000..a0c0703 --- /dev/null +++ b/etcd/src/main/java/org/apache/tamaya/etcd/EtcdBackends.java @@ -0,0 +1,65 @@ +/* + * 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 java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Singleton that reads and stores the current etcd setup, especially the possible URLs to be used. + */ +public final class EtcdBackends { + + private static final Logger LOG = Logger.getLogger(EtcdBackends.class.getName()); + private static List<EtcdAccessor> etcdBackends = new ArrayList<>(); + + static{ + int timeout = 2; + String val = System.getProperty("tamaya.etcd.timeout"); + if(val == null){ + val = System.getenv("tamaya.etcd.timeout"); + } + if(val!=null){ + timeout = Integer.parseInt(val); + } + 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.trim(), timeout)); + LOG.info("Using etcd endoint: " + url); + } catch(Exception e){ + LOG.log(Level.SEVERE, "Error initializing etcd accessor for URL: " + url, e); + } + } + } + + private EtcdBackends(){} + + public static List<EtcdAccessor> getEtcdBackends(){ + return etcdBackends; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java ---------------------------------------------------------------------- diff --git a/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java b/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java new file mode 100644 index 0000000..5e129f7 --- /dev/null +++ b/etcd/src/main/java/org/apache/tamaya/etcd/EtcdPropertySource.java @@ -0,0 +1,209 @@ +/* + * 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.mutableconfig.spi.ConfigChangeRequest; +import org.apache.tamaya.mutableconfig.spi.MutablePropertySource; +import org.apache.tamaya.spi.PropertyValue; +import org.apache.tamaya.spi.PropertyValueBuilder; +import org.apache.tamaya.spisupport.BasePropertySource; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * 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. + * ETcd can be disabled by setting {@code tamaya.etcdprops.disable} either as env or system property. + */ +public class EtcdPropertySource extends BasePropertySource + implements MutablePropertySource{ + private static final Logger LOG = Logger.getLogger(EtcdPropertySource.class.getName()); + + private String prefix = System.getProperty("tamaya.etcd.prefix", ""); + + private final boolean disabled = evaluateDisabled(); + + private boolean evaluateDisabled() { + String value = System.getProperty("tamaya.etcdprops.disable"); + if(value==null){ + value = System.getenv("tamaya.etcdprops.disable"); + } + if(value==null){ + return false; + } + return value.isEmpty() || Boolean.parseBoolean(value); + } + + @Override + public int getOrdinal() { + PropertyValue configuredOrdinal = get(TAMAYA_ORDINAL); + if(configuredOrdinal!=null){ + try{ + return Integer.parseInt(configuredOrdinal.getValue()); + } 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 1000; + } + + @Override + public String getName() { + return "etcd"; + } + + @Override + public PropertyValue get(String key) { + if(disabled){ + return null; + } + // check prefix, if key does not start with it, it is not part of our name space + // if so, the prefix part must be removedProperties, so etcd can resolve without it + if(!key.startsWith(prefix)){ + return null; + } else{ + key = key.substring(prefix.length()); + } + Map<String,String> props; + 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.getEtcdBackends()){ + try{ + props = accessor.get(reqKey); + if(!props.containsKey("_ERROR")) { + // No repfix mapping necessary here, since we only access/return the value... + return new PropertyValueBuilder(key, props.get(reqKey), getName()).setContextData(props).build(); + } 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 null; + } + + @Override + public Map<String, String> getProperties() { + if(disabled){ + return Collections.emptyMap(); + } + if(!EtcdBackends.getEtcdBackends().isEmpty()){ + for(EtcdAccessor accessor: EtcdBackends.getEtcdBackends()){ + 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; + } + + @Override + public void applyChange(ConfigChangeRequest configChange) { + for(EtcdAccessor accessor: EtcdBackends.getEtcdBackends()){ + try{ + for(String k: configChange.getRemovedProperties()){ + Map<String,String> res = accessor.delete(k); + if(res.get("_ERROR")!=null){ + LOG.info("Failed to remove key from etcd: " + k); + } + } + for(Map.Entry<String,String> en:configChange.getAddedProperties().entrySet()){ + String key = en.getKey(); + Integer ttl = null; + int index = en.getKey().indexOf('?'); + if(index>0){ + key = en.getKey().substring(0, index); + String rawQuery = en.getKey().substring(index+1); + String[] queries = rawQuery.split("&"); + for(String query:queries){ + if(query.contains("ttl")){ + int qIdx = query.indexOf('='); + ttl = qIdx>0?Integer.parseInt(query.substring(qIdx+1).trim()):null; + } + } + } + Map<String,String> res = accessor.set(key, en.getValue(), ttl); + if(res.get("_ERROR")!=null){ + LOG.info("Failed to add key to etcd: " + en.getKey() + "=" + en.getValue()); + } + } + // success, stop here + break; + } catch(Exception e){ + LOG.log(Level.FINE, "etcd access failed on " + accessor.getUrl() + ", trying next...", e); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/etcd/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertySource ---------------------------------------------------------------------- diff --git a/etcd/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertySource b/etcd/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertySource new file mode 100644 index 0000000..eb7958e --- /dev/null +++ b/etcd/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertySource @@ -0,0 +1,19 @@ +# +# 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. +# +org.apache.tamaya.etcd.EtcdPropertySource \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/358828fe/etcd/src/test/java/org/apache/tamaya/etcd/EtcdAccessorTest.java ---------------------------------------------------------------------- diff --git a/etcd/src/test/java/org/apache/tamaya/etcd/EtcdAccessorTest.java b/etcd/src/test/java/org/apache/tamaya/etcd/EtcdAccessorTest.java new file mode 100644 index 0000000..80bd716 --- /dev/null +++ b/etcd/src/test/java/org/apache/tamaya/etcd/EtcdAccessorTest.java @@ -0,0 +1,116 @@ +/* + * 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.junit.BeforeClass; + +import java.net.MalformedURLException; +import java.util.Map; +import java.util.UUID; + +import static org.junit.Assert.*; + +/** + * Tests for th etcd backend integration. You must have set a system property so, theses tests are executed, e.g. + * {@code -Detcd.url=http://127.0.0.1:4001}. + */ +public class EtcdAccessorTest { + + private static EtcdAccessor accessor; + static boolean execute = false; + + @BeforeClass + public static void setup() throws MalformedURLException { + 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.server.urls")); + System.out.println("Configure etcd with -Detcd.server.urls=http://<IP>:<PORT>"); + } + else{ + execute = true; + } + } + + @org.junit.Test + public void testGetVersion() throws Exception { + if(!execute)return; + assertEquals(accessor.getVersion(), "etcd 0.4.9"); + } + + @org.junit.Test + public void testGet() throws Exception { + if(!execute)return; + Map<String,String> result = accessor.get("test1"); + assertNotNull(result); + } + + @org.junit.Test + public void testSetNormal() throws Exception { + if(!execute)return; + String value = UUID.randomUUID().toString(); + Map<String,String> result = accessor.set("testSetNormal", value); + assertNull(result.get("_testSetNormal.ttl")); + assertEquals(accessor.get("testSetNormal").get("testSetNormal"), value); + } + + @org.junit.Test + public void testSetNormal2() throws Exception { + if(!execute)return; + String value = UUID.randomUUID().toString(); + Map<String,String> result = accessor.set("testSetNormal2", value, null); + assertNull(result.get("_testSetNormal2.ttl")); + assertEquals(accessor.get("testSetNormal2").get("testSetNormal2"), value); + } + + @org.junit.Test + public void testSetWithTTL() throws Exception { + if(!execute)return; + String value = UUID.randomUUID().toString(); + Map<String,String> result = accessor.set("testSetWithTTL", value, 1); + assertNotNull(result.get("_testSetWithTTL.ttl")); + assertEquals(accessor.get("testSetWithTTL").get("testSetWithTTL"), value); + Thread.sleep(2000L); + result = accessor.get("testSetWithTTL"); + assertNull(result.get("testSetWithTTL")); + } + + + @org.junit.Test + public void testDelete() throws Exception { + if(!execute)return; + String value = UUID.randomUUID().toString(); + Map<String,String> result = accessor.set("testDelete", value, null); + assertEquals(accessor.get("testDelete").get("testDelete"), value); + assertNotNull(result.get("_testDelete.createdIndex")); + result = accessor.delete("testDelete"); + assertEquals(result.get("_testDelete.prevNode.value"),value); + assertNull(accessor.get("testDelete").get("testDelete")); + } + + @org.junit.Test + public void testGetProperties() throws Exception { + if(!execute)return; + String value = UUID.randomUUID().toString(); + accessor.set("testGetProperties1", value); + Map<String,String> result = accessor.getProperties(""); + assertNotNull(result); + assertEquals(result.get("testGetProperties1"), value); + assertNotNull(result.get("_testGetProperties1.createdIndex")); + } +} \ No newline at end of file
