This is an automated email from the ASF dual-hosted git repository.

liujun pushed a commit to branch dev-metadata
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git

commit 6f618226c179f76cf921f4792df6878320917ef1
Author: ken.lj <ken.lj...@gmail.com>
AuthorDate: Tue Nov 27 17:29:09 2018 +0800

    try different zookeeper implementations
---
 .../configcenter/AbstractDynamicConfiguration.java |  31 +++--
 .../dubbo/configcenter/ConfigChangeEvent.java      |  16 +--
 .../dubbo/configcenter/DynamicConfiguration.java   |   2 +
 .../support/nop/NopDynamicConfiguration.java       |   9 +-
 .../mock/MockDynamicConfiguration.java             |  23 ++++
 ....apache.dubbo.configcenter.DynamicConfiguration |   1 +
 .../support/apollo/ApolloDynamicConfiguration.java |  48 ++++---
 .../pom.xml                                        |   4 +-
 .../zookeeper}/ArchaiusDynamicConfiguration.java   |  60 ++++-----
 .../sources/ZooKeeperConfigurationSource.java      |   4 +-
 ....apache.dubbo.configcenter.DynamicConfiguration |   1 +
 .../pom.xml                                        |  16 +--
 .../support/zookeeper/CacheListener.java           | 107 +++++++++++++++
 .../zookeeper/ZookeeperDynamicConfiguration.java   | 144 ++++++++++++++++++++
 ....apache.dubbo.configcenter.DynamicConfiguration |   1 +
 .../ZookeeperDynamicConfigurationTest.java         | 150 +++++++++++++++++++++
 .../dubbo-configcenter-zookeeper/pom.xml           |  30 +++--
 ....apache.dubbo.configcenter.DynamicConfiguration |   3 +-
 .../ZookeeperDynamicConfigurationTest.java         | 150 +++++++++++++++++++++
 dubbo-configcenter/pom.xml                         |   6 +-
 20 files changed, 699 insertions(+), 107 deletions(-)

diff --git 
a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/AbstractDynamicConfiguration.java
 
b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/AbstractDynamicConfiguration.java
index 258ce52..6fcf8c3 100644
--- 
a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/AbstractDynamicConfiguration.java
+++ 
b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/AbstractDynamicConfiguration.java
@@ -26,8 +26,8 @@ import java.util.concurrent.ConcurrentMap;
  * Dynamic configuration template class. The concrete implementation needs to 
provide implementation for three methods.
  *
  * @see AbstractDynamicConfiguration#getTargetConfig(String, String, long)
- * @see AbstractDynamicConfiguration#addListener(String, ConfigurationListener)
- * @see AbstractDynamicConfiguration#createTargetListener(String, 
ConfigurationListener)
+ * @see AbstractDynamicConfiguration#addConfigurationListener(TargetListener, 
ConfigurationListener)
+ * @see AbstractDynamicConfiguration#createTargetListener(String)
  */
 public abstract class AbstractDynamicConfiguration<TargetListener> extends 
AbstractConfiguration
         implements DynamicConfiguration {
@@ -36,7 +36,7 @@ public abstract class 
AbstractDynamicConfiguration<TargetListener> extends Abstr
     protected URL url;
 
     // One key can register multiple target listeners, but one target listener 
only maps to one configuration listener
-    private ConcurrentMap<String, ConcurrentMap<ConfigurationListener, 
TargetListener>> targetListeners =
+    protected ConcurrentMap<String, TargetListener> targetListeners =
             new ConcurrentHashMap<>();
 
     public AbstractDynamicConfiguration() {
@@ -49,11 +49,8 @@ public abstract class 
AbstractDynamicConfiguration<TargetListener> extends Abstr
 
     @Override
     public void addListener(String key, ConfigurationListener listener) {
-        ConcurrentMap<ConfigurationListener, TargetListener> listeners = 
targetListeners.computeIfAbsent(key,
-                k -> new ConcurrentHashMap<>());
-        TargetListener targetListener = listeners.computeIfAbsent(listener,
-                k -> createTargetListener(key, listener));
-        addTargetListener(key, targetListener);
+        TargetListener targetListener = targetListeners.computeIfAbsent(key, 
this::createTargetListener);
+        addConfigurationListener(key, targetListener, listener);
     }
 
     @Override
@@ -88,6 +85,13 @@ public abstract class 
AbstractDynamicConfiguration<TargetListener> extends Abstr
         }
     }
 
+    @Override
+    public void removeListener(String key) {
+
+    }
+
+    protected abstract void recover();
+
     /**
      * Fetch dynamic configuration from backend config storage. If timeout 
exceeds, exception should be thrown.
      *
@@ -101,20 +105,19 @@ public abstract class 
AbstractDynamicConfiguration<TargetListener> extends Abstr
     /**
      * Register a native listener to the backend config storage so that Dubbo 
has chance to get notified when the
      * value changes.
-     *
-     * @param key      property key listener is interested.
-     * @param listener native listener for the backend config storage
+     * @param key
+     * @param targetListener Implementation dependent listener, such as, 
zookeeper watcher, Apollo listener, ...
+     * @param configurationListener Listener in Dubbo that will handle 
notification.
      */
-    protected abstract void addTargetListener(String key, TargetListener 
listener);
+    protected abstract void addConfigurationListener(String key, 
TargetListener targetListener, ConfigurationListener configurationListener);
 
     /**
      * Create a native listener for the backend config storage, eventually 
ConfigurationListener will get notified once
      * the value changes.
      *
      * @param key      property key the native listener will listen on
-     * @param listener ConfigurationListener instance
      * @return native listener for the backend config storage
      */
-    protected abstract TargetListener createTargetListener(String key, 
ConfigurationListener listener);
+    protected abstract TargetListener createTargetListener(String key);
 
 }
diff --git 
a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/ConfigChangeEvent.java
 
b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/ConfigChangeEvent.java
index 403ec75..068f21d 100644
--- 
a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/ConfigChangeEvent.java
+++ 
b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/ConfigChangeEvent.java
@@ -25,16 +25,14 @@ public class ConfigChangeEvent {
     private String key;
     private String newValue;
     private ConfigChangeType changeType;
-    private ConfigType type;
 
-    public ConfigChangeEvent(String key, String value, ConfigType type) {
-        this(key, value, type, ConfigChangeType.MODIFIED);
+    public ConfigChangeEvent(String key, String value) {
+        this(key, value, ConfigChangeType.MODIFIED);
     }
 
-    public ConfigChangeEvent(String key, String value, ConfigType type, 
ConfigChangeType changeType) {
+    public ConfigChangeEvent(String key, String value, ConfigChangeType 
changeType) {
         this.key = key;
         this.newValue = value;
-        this.type = type;
         this.changeType = changeType;
     }
 
@@ -61,12 +59,4 @@ public class ConfigChangeEvent {
     public void setChangeType(ConfigChangeType changeType) {
         this.changeType = changeType;
     }
-
-    public ConfigType getType() {
-        return type;
-    }
-
-    public void setType(ConfigType type) {
-        this.type = type;
-    }
 }
diff --git 
a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java
 
b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java
index 83faee6..83ff936 100644
--- 
a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java
+++ 
b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java
@@ -41,6 +41,8 @@ public interface DynamicConfiguration extends Configuration {
      */
     void addListener(String key, ConfigurationListener listener);
 
+    void removeListener(String key);
+
     /**
      * Get the configuration mapped to the given key
      *
diff --git 
a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/support/nop/NopDynamicConfiguration.java
 
b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/support/nop/NopDynamicConfiguration.java
index 1dc0794..1250233 100644
--- 
a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/support/nop/NopDynamicConfiguration.java
+++ 
b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/support/nop/NopDynamicConfiguration.java
@@ -32,12 +32,12 @@ public class NopDynamicConfiguration extends 
AbstractDynamicConfiguration {
     }
 
     @Override
-    protected void addTargetListener(String key, Object o) {
+    protected void addConfigurationListener(String key, Object targetListener, 
ConfigurationListener configurationListener) {
         // no-op
     }
 
     @Override
-    protected Object createTargetListener(String key, ConfigurationListener 
listener) {
+    protected Object createTargetListener(String key) {
         return null;
     }
 
@@ -45,4 +45,9 @@ public class NopDynamicConfiguration extends 
AbstractDynamicConfiguration {
     protected Object getInternalProperty(String key) {
         return null;
     }
+
+    @Override
+    protected void recover() {
+
+    }
 }
diff --git 
a/dubbo-configcenter/dubbo-configcenter-api/src/test/java/org/apache/dubbo/configcenter/mock/MockDynamicConfiguration.java
 
b/dubbo-configcenter/dubbo-configcenter-api/src/test/java/org/apache/dubbo/configcenter/mock/MockDynamicConfiguration.java
new file mode 100644
index 0000000..012322d
--- /dev/null
+++ 
b/dubbo-configcenter/dubbo-configcenter-api/src/test/java/org/apache/dubbo/configcenter/mock/MockDynamicConfiguration.java
@@ -0,0 +1,23 @@
+/*
+ * 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.dubbo.configcenter.mock;
+
+/**
+ *
+ */
+public class MockDynamicConfiguration {
+}
diff --git 
a/dubbo-configcenter/dubbo-configcenter-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
 
b/dubbo-configcenter/dubbo-configcenter-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
new file mode 100644
index 0000000..944a089
--- /dev/null
+++ 
b/dubbo-configcenter/dubbo-configcenter-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
@@ -0,0 +1 @@
+mock=org.apache.dubbo.configcenter.support.mock.MockDynamicConfiguration
\ No newline at end of file
diff --git 
a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
 
b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
index 8c056dc..0c0d95c 100644
--- 
a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
+++ 
b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
@@ -22,7 +22,6 @@ import com.ctrip.framework.apollo.ConfigService;
 import com.ctrip.framework.apollo.enums.ConfigSourceType;
 import com.ctrip.framework.apollo.enums.PropertyChangeType;
 import com.ctrip.framework.apollo.model.ConfigChange;
-
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
@@ -33,15 +32,13 @@ import org.apache.dubbo.configcenter.ConfigChangeEvent;
 import org.apache.dubbo.configcenter.ConfigChangeType;
 import org.apache.dubbo.configcenter.ConfigurationListener;
 
-import java.util.Collections;
-
-import static org.apache.dubbo.configcenter.ConfigType.CONFIGURATORS;
-import static org.apache.dubbo.configcenter.ConfigType.ROUTERS;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
 
 /**
  * Apollo implementation, https://github.com/ctripcorp/apollo
  */
-public class ApolloDynamicConfiguration extends 
AbstractDynamicConfiguration<ConfigChangeListener> {
+public class ApolloDynamicConfiguration extends 
AbstractDynamicConfiguration<ApolloDynamicConfiguration.ApolloListener> {
     private static final Logger logger = 
LoggerFactory.getLogger(ApolloDynamicConfiguration.class);
     private static final String APOLLO_ENV_KEY = "env";
     private static final String APOLLO_ADDR_KEY = "apollo.meta";
@@ -104,7 +101,7 @@ public class ApolloDynamicConfiguration extends 
AbstractDynamicConfiguration<Con
     }
 
     /**
-     * This method will used by Configuration to get valid value at runtime.
+     * This method will be used by Configuration to get valid value at runtime.
      * The group is expected to be 'app level', which can be fetched from the 
'config.appnamespace' in url if necessary.
      * But I think Apollo's inheritance feature of namespace can solve the 
problem .
      */
@@ -114,21 +111,31 @@ public class ApolloDynamicConfiguration extends 
AbstractDynamicConfiguration<Con
     }
 
     @Override
-    protected void addTargetListener(String key, ConfigChangeListener 
listener) {
-        this.dubboConfig.addChangeListener(listener, 
Collections.singleton(key));
+    protected void addConfigurationListener(String key, ApolloListener 
listener, ConfigurationListener configurationListener) {
+        listener.addListener(configurationListener);
+    }
+
+    @Override
+    protected ApolloListener createTargetListener(String key) {
+        ApolloListener listener = new ApolloListener();
+        this.dubboConfig.addChangeListener(listener);
+        return listener;
     }
 
     @Override
-    protected ConfigChangeListener createTargetListener(String key, 
ConfigurationListener listener) {
-        return new ApolloListener(listener);
+    protected void recover() {
+        // Apollo will handle things well.
     }
 
-    private class ApolloListener implements ConfigChangeListener {
+    public class ApolloListener implements ConfigChangeListener {
 
-        private ConfigurationListener listener;
+        private Set<ConfigurationListener> listeners = new 
CopyOnWriteArraySet<>();
 
-        ApolloListener(ConfigurationListener listener) {
-            this.listener = listener;
+        ApolloListener() {
+        }
+
+        public void addListener(ConfigurationListener configurationListener) {
+            this.listeners.add(configurationListener);
         }
 
         @Override
@@ -140,13 +147,10 @@ public class ApolloDynamicConfiguration extends 
AbstractDynamicConfiguration<Con
                             change.getOldValue() + ", the empty rule will not 
take effect.");
                     return;
                 }
-                // TODO Maybe we no longer need to identify the type of 
change. Because there's no scenario that
-                // a callback will subscribe for both configurators and routers
-                if 
(change.getPropertyName().endsWith(Constants.CONFIGURATORS_SUFFIX)) {
-                    listener.process(new ConfigChangeEvent(key, 
change.getNewValue(), CONFIGURATORS, getChangeType(change)));
-                } else {
-                    listener.process(new ConfigChangeEvent(key, 
change.getNewValue(), ROUTERS, getChangeType(change)));
-                }
+
+                listeners.forEach(
+                        listener -> listener.process(new 
ConfigChangeEvent(key, change.getNewValue(), getChangeType(change)))
+                );
             }
         }
 
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml 
b/dubbo-configcenter/dubbo-configcenter-archaius/pom.xml
similarity index 94%
copy from dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
copy to dubbo-configcenter/dubbo-configcenter-archaius/pom.xml
index 8e3d7fe..a122d72 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
+++ b/dubbo-configcenter/dubbo-configcenter-archaius/pom.xml
@@ -22,10 +22,10 @@
         <artifactId>dubbo-configcenter</artifactId>
         <version>2.7.0-SNAPSHOT</version>
     </parent>
-    <artifactId>dubbo-configcenter-zookeeper</artifactId>
+    <artifactId>dubbo-configcenter-archaius</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <description>The zookeeper implementation of the configcenter 
api</description>
+    <description>The archaius implementation of the config-center 
api</description>
 
     <dependencies>
         <dependency>
diff --git 
a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/ArchaiusDynamicConfiguration.java
 
b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ArchaiusDynamicConfiguration.java
similarity index 74%
rename from 
dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/ArchaiusDynamicConfiguration.java
rename to 
dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ArchaiusDynamicConfiguration.java
index d739009..e0c64e3 100644
--- 
a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/ArchaiusDynamicConfiguration.java
+++ 
b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ArchaiusDynamicConfiguration.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.configcenter.support.archaius;
+package org.apache.dubbo.configcenter.support.zookeeper;
 
 import com.netflix.config.ConfigurationManager;
 import com.netflix.config.DynamicPropertyFactory;
@@ -24,26 +24,29 @@ import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.configcenter.AbstractDynamicConfiguration;
 import org.apache.dubbo.configcenter.ConfigChangeEvent;
 import org.apache.dubbo.configcenter.ConfigChangeType;
 import org.apache.dubbo.configcenter.ConfigType;
 import org.apache.dubbo.configcenter.ConfigurationListener;
-import 
org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource;
+import 
org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource;
+
+import java.util.Set;
 
 import static org.apache.dubbo.common.Constants.CONFIG_NAMESPACE_KEY;
-import static 
org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource.ARCHAIUS_CONFIG_CHECK_KEY;
-import static 
org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource.ARCHAIUS_CONFIG_ROOT_PATH_KEY;
-import static 
org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource.ARCHAIUS_SOURCE_ADDRESS_KEY;
-import static 
org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource.DEFAULT_CONFIG_ROOT_PATH;
+import static 
org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource.ARCHAIUS_CONFIG_CHECK_KEY;
+import static 
org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource.ARCHAIUS_CONFIG_ROOT_PATH_KEY;
+import static 
org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource.ARCHAIUS_SOURCE_ADDRESS_KEY;
+import static 
org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource.DEFAULT_CONFIG_ROOT_PATH;
 
 /**
  * Archaius supports various sources and it's extensiable: JDBC, ZK, 
Properties, ..., so should we make it extensiable?
  * FIXME: we should get rid of Archaius or move it to eco system since 
Archaius is out of maintenance, instead, we
  * should rely on curator-x-async which looks quite promising.
  */
-public class ArchaiusDynamicConfiguration extends 
AbstractDynamicConfiguration<Runnable> {
+public class ArchaiusDynamicConfiguration extends 
AbstractDynamicConfiguration<ArchaiusDynamicConfiguration.ArchaiusListener> {
     private static final Logger logger = 
LoggerFactory.getLogger(ArchaiusDynamicConfiguration.class);
 
     public ArchaiusDynamicConfiguration() {
@@ -107,37 +110,35 @@ public class ArchaiusDynamicConfiguration extends 
AbstractDynamicConfiguration<R
     }
 
     @Override
-    protected void addTargetListener(String key, Runnable runnable) {
+    protected void addConfigurationListener(String key, ArchaiusListener 
targetListener, ConfigurationListener configurationListener) {
+        targetListener.addListener(configurationListener);
+    }
+
+    @Override
+    protected ArchaiusListener createTargetListener(String key) {
+        ArchaiusListener archaiusListener = new ArchaiusListener(key);
         DynamicStringProperty prop = DynamicPropertyFactory.getInstance()
                 .getStringProperty(key, null);
-        prop.addCallback(runnable);
+        prop.addCallback(archaiusListener);
+        return archaiusListener;
     }
 
     @Override
-    protected Runnable createTargetListener(String key, ConfigurationListener 
listener) {
-        return new ArchaiusListener(key, listener);
+    protected void recover() {
+        // FIXME will Archaius recover automatically?
     }
 
-    private class ArchaiusListener implements Runnable {
-        private ConfigurationListener listener;
+    public class ArchaiusListener implements Runnable {
+        private Set<ConfigurationListener> listeners = new 
ConcurrentHashSet<>();
         private String key;
         private ConfigType type;
 
-        public ArchaiusListener(String key, ConfigurationListener listener) {
+        public ArchaiusListener(String key) {
             this.key = key;
-            this.listener = listener;
-            // Maybe we no longer need to identify the type of change. Because 
there's no scenario that a callback
-            // will subscribe for both configurators and routers
-            if (key.endsWith(Constants.CONFIGURATORS_SUFFIX)) {
-                type = ConfigType.CONFIGURATORS;
-            } else {
-                /**
-                 * used for all router rules:
-                 * {@link Constants.ROUTERS_SUFFIX}
-                 * {@link 
org.apache.dubbo.rpc.cluster.router.tag.TagRouter.TAGRULE_DATAID}
-                 */
-                type = ConfigType.ROUTERS;
-            }
+        }
+
+        public void addListener(ConfigurationListener listener) {
+            this.listeners.add(listener);
         }
 
         @Override
@@ -145,10 +146,9 @@ public class ArchaiusDynamicConfiguration extends 
AbstractDynamicConfiguration<R
             DynamicStringProperty prop = DynamicPropertyFactory.getInstance()
                     .getStringProperty(key, null);
             String newValue = prop.get();
-            ConfigChangeEvent event = new ConfigChangeEvent(key, newValue, 
type);
+            ConfigChangeEvent event = new ConfigChangeEvent(key, newValue);
             if (newValue == null) {
                 event.setChangeType(ConfigChangeType.DELETED);
-                listener.process(event);
             } else {
                 if (newValue.equals("")) {
                     logger.warn("an empty rule is received for " + key + ", 
the current working rule is unknown, " +
@@ -156,8 +156,8 @@ public class ArchaiusDynamicConfiguration extends 
AbstractDynamicConfiguration<R
                     return;
                 }
                 event.setChangeType(ConfigChangeType.MODIFIED);
-                listener.process(event);
             }
+            listeners.forEach(listener -> listener.process(event));
         }
     }
 }
diff --git 
a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/sources/ZooKeeperConfigurationSource.java
 
b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/sources/ZooKeeperConfigurationSource.java
similarity index 98%
rename from 
dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/sources/ZooKeeperConfigurationSource.java
rename to 
dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/sources/ZooKeeperConfigurationSource.java
index c7d5082..e77e434 100644
--- 
a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/sources/ZooKeeperConfigurationSource.java
+++ 
b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/sources/ZooKeeperConfigurationSource.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.configcenter.support.archaius.sources;
+package org.apache.dubbo.configcenter.support.zookeeper.sources;
 
 import com.google.common.io.Closeables;
 import com.netflix.config.WatchedConfigurationSource;
@@ -185,7 +185,7 @@ public class ZooKeeperConfigurationSource implements 
WatchedConfigurationSource,
             }
         }, executor);
 
-        // passing true to trigger an initial rebuild upon starting.  
(blocking call)
+        // it's not blocking, so we use an extra latch 'initializedLatch' to 
make sure cache fully initialized before use.
         treeCache.start();
     }
 
diff --git 
a/dubbo-configcenter/dubbo-configcenter-archaius/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
 
b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
new file mode 100644
index 0000000..d271f16
--- /dev/null
+++ 
b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
@@ -0,0 +1 @@
+archaius=org.apache.dubbo.configcenter.support.archaius.ArchaiusDynamicConfiguration
\ No newline at end of file
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml 
b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/pom.xml
similarity index 81%
copy from dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
copy to dubbo-configcenter/dubbo-configcenter-zookeeper-cache/pom.xml
index 8e3d7fe..191b4ae 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/pom.xml
@@ -22,10 +22,10 @@
         <artifactId>dubbo-configcenter</artifactId>
         <version>2.7.0-SNAPSHOT</version>
     </parent>
-    <artifactId>dubbo-configcenter-zookeeper</artifactId>
+    <artifactId>dubbo-configcenter-zookeeper-cache</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <description>The zookeeper implementation of the configcenter 
api</description>
+    <description>The zookeeper implementation of the config-center 
api</description>
 
     <dependencies>
         <dependency>
@@ -34,20 +34,20 @@
             <version>${project.parent.version}</version>
         </dependency>
         <dependency>
-            <groupId>com.netflix.archaius</groupId>
-            <artifactId>archaius-core</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.apache.curator</groupId>
             <artifactId>curator-framework</artifactId>
+            <version>2.12.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.curator</groupId>
             <artifactId>curator-recipes</artifactId>
+            <version>2.12.0</version>
         </dependency>
         <dependency>
-            <groupId>commons-configuration</groupId>
-            <artifactId>commons-configuration</artifactId>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-test</artifactId>
+            <version>2.12.0</version>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git 
a/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java
 
b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java
new file mode 100644
index 0000000..bbfa057
--- /dev/null
+++ 
b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java
@@ -0,0 +1,107 @@
+/*
+ * 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.dubbo.configcenter.support.zookeeper;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.cache.ChildData;
+import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
+import org.apache.curator.framework.recipes.cache.TreeCacheListener;
+import org.apache.dubbo.common.config.Configuration;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.configcenter.ConfigChangeEvent;
+import org.apache.dubbo.configcenter.ConfigChangeType;
+import org.apache.dubbo.configcenter.ConfigurationListener;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ *
+ */
+public class CacheListener implements TreeCacheListener {
+    private Map<String, Set<ConfigurationListener>> listeners = new 
ConcurrentHashMap<>();
+    private CountDownLatch initializedLatch;
+    private String rootPath;
+
+    public CacheListener(String rootPath, CountDownLatch initializedLatch) {
+        this.rootPath = rootPath;
+        this.initializedLatch = initializedLatch;
+    }
+
+    @Override
+    public void childEvent(CuratorFramework aClient, TreeCacheEvent event) 
throws Exception {
+
+        TreeCacheEvent.Type type = event.getType();
+        ChildData data = event.getData();
+        if (type == TreeCacheEvent.Type.INITIALIZED) {
+            initializedLatch.countDown();
+            return;
+        }
+
+        // TODO, ignore other event types
+        if (data == null) {
+            return;
+        }
+
+        // TODO We limit the notification of config changes to a specific path 
level, for example
+        //  /dubbo/config/service/configurators, other config changes not in 
this level will not get notified,
+        //  say /dubbo/config/dubbo.properties
+        if (data.getPath().split("/").length == 5) {
+            byte[] value = data.getData();
+            String key = pathToKey(data.getPath());
+            ConfigChangeType changeType;
+            switch (type) {
+                case NODE_ADDED:
+                    changeType = ConfigChangeType.ADDED;
+                    break;
+                case NODE_REMOVED:
+                    changeType = ConfigChangeType.DELETED;
+                    break;
+                case NODE_UPDATED:
+                    changeType = ConfigChangeType.MODIFIED;
+                    break;
+                default:
+                    return;
+            }
+
+            ConfigChangeEvent configChangeEvent = new ConfigChangeEvent(key, 
new String(value, StandardCharsets.UTF_8), changeType);
+            listeners.get(key).forEach(listener -> 
listener.process(configChangeEvent));
+        }
+    }
+
+    public void addListener(String key, ConfigurationListener 
configurationListener) {
+        Set<ConfigurationListener> set = this.listeners.computeIfAbsent(key, k 
-> new CopyOnWriteArraySet<>());
+        set.add(configurationListener);
+    }
+
+    /**
+     * This is used to convert a configuration nodePath into a key
+     *
+     * @param path
+     * @return key (nodePath less the config root path)
+     */
+    private String pathToKey(String path) {
+        if (StringUtils.isEmpty(path)) {
+            return path;
+        }
+        return path.replace(rootPath + "/", "").replaceAll("/", ".");
+    }
+}
diff --git 
a/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
 
b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
new file mode 100644
index 0000000..6e78079
--- /dev/null
+++ 
b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
@@ -0,0 +1,144 @@
+/*
+ * 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.dubbo.configcenter.support.zookeeper;
+
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.cache.ChildData;
+import org.apache.curator.framework.recipes.cache.TreeCache;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.dubbo.common.Constants;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.configcenter.AbstractDynamicConfiguration;
+import org.apache.dubbo.configcenter.ConfigurationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.curator.framework.CuratorFrameworkFactory.newClient;
+import static org.apache.dubbo.common.Constants.CONFIG_NAMESPACE_KEY;
+
+/**
+ *
+ */
+public class ZookeeperDynamicConfiguration extends 
AbstractDynamicConfiguration<CacheListener> {
+    private static final Logger logger = 
LoggerFactory.getLogger(ZookeeperDynamicConfiguration.class);
+    private Executor executor = Executors.newFixedThreadPool(1);
+    private CuratorFramework client;
+
+    // The final root path would be: /configRootPath/"config"
+    private String rootPath;
+    private TreeCache treeCache;
+    private CountDownLatch initializedLatch = new CountDownLatch(1);
+
+    private CacheListener cacheListener;
+
+    @Override
+    public void initWith(URL url) {
+        super.initWith(url);
+
+        rootPath =  "/" + url.getParameter(CONFIG_NAMESPACE_KEY, 
Constants.DUBBO) + "/config";
+
+        RetryPolicy policy = new ExponentialBackoffRetry(1000, 3);
+        int sessionTimeout = 60 * 1000;
+        int connectTimeout = 10 * 1000;
+        String connectString = url.getBackupAddress();
+        client = newClient(connectString, sessionTimeout, connectTimeout, 
policy);
+        client.start();
+
+        try {
+            boolean connected = client.blockUntilConnected(3 * connectTimeout, 
TimeUnit.MILLISECONDS);
+            if (!connected) {
+                if (url.getParameter(Constants.CONFIG_CHECK_KEY, false)) {
+                    throw new IllegalStateException("Failed to connect to 
config center (zookeeper): "
+                            + connectString + " in " + 3 * connectTimeout + 
"ms.");
+                } else {
+                    logger.warn("The config center (zookeeper) is not fully 
initialized in " + 3 * connectTimeout + "ms, address is: " + connectString);
+                }
+            }
+        } catch (InterruptedException e) {
+            throw new IllegalStateException("The thread was interrupted 
unexpectedly when trying connecting to zookeeper "
+                    + connectString + " config center, ", e);
+        }
+
+        this.cacheListener = new CacheListener(rootPath, initializedLatch);
+
+        // build local cache
+        try {
+            this.buildCache();
+        } catch (Exception e) {
+            logger.warn("Failed to build local cache for config center 
(zookeeper), address is ." + connectString);
+        }
+    }
+
+    @Override
+    protected String getTargetConfig(String key, String group, long timeout) {
+        if (StringUtils.isNotEmpty(group)) {
+            key = group + "." + key;
+        }
+
+        return (String) getInternalProperty(key);
+    }
+
+    @Override
+    protected void addConfigurationListener(String key, CacheListener 
cacheListener, ConfigurationListener listener) {
+        cacheListener.addListener(key, listener);
+    }
+
+    @Override
+    protected CacheListener createTargetListener(String key) {
+        return cacheListener;
+    }
+
+    /**
+     *
+     * @param key e.g., {service}.configurators, {service}.tagrouters, 
{group}.dubbo.properties
+     * @return
+     */
+    @Override
+    protected Object getInternalProperty(String key) {
+        ChildData childData = treeCache.getCurrentData(rootPath + "/" + 
key.replaceFirst("\\.", "/"));
+        if (childData != null) {
+            return new String(childData.getData(), StandardCharsets.UTF_8);
+        }
+        return null;
+    }
+
+    protected void recover() {
+
+    }
+
+    /**
+     * Adds a listener to the pathChildrenCache, initializes the cache, then 
starts the cache-management background
+     * thread
+     */
+    private void buildCache() throws Exception {
+        this.treeCache = new TreeCache(client, rootPath);
+        // create the watcher for future configuration updates
+        treeCache.getListenable().addListener(cacheListener, executor);
+
+        // it's not blocking, so we use an extra latch 'initializedLatch' to 
make sure cache fully initialized before use.
+        treeCache.start();
+        initializedLatch.await();
+    }
+}
diff --git 
a/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
 
b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
new file mode 100644
index 0000000..0379c0e
--- /dev/null
+++ 
b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
@@ -0,0 +1 @@
+zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfiguration
\ No newline at end of file
diff --git 
a/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
 
b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
new file mode 100644
index 0000000..382e77d
--- /dev/null
+++ 
b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.dubbo.configcenter.support.zookeeper;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.test.TestingServer;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.configcenter.ConfigChangeEvent;
+import org.apache.dubbo.configcenter.ConfigurationListener;
+import org.apache.dubbo.configcenter.DynamicConfiguration;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ *
+ */
+public class ZookeeperDynamicConfigurationTest {
+    private static CuratorFramework client;
+
+    private static URL configUrl;
+    private static int zkServerPort = NetUtils.getAvailablePort();
+    private static TestingServer zkServer;
+    private static DynamicConfiguration configuration;
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        zkServer = new TestingServer(zkServerPort, true);
+
+        client = CuratorFrameworkFactory.newClient("localhost:" + 
zkServerPort, 60 * 1000, 60 * 1000,
+                new ExponentialBackoffRetry(1000, 3));
+        client.start();
+
+        try {
+            setData("/dubbo/config/dubbo/dubbo.properties", "The content from 
dubbo.properties");
+            setData("/dubbo/config/group*service:version/configurators", "The 
content from configurators");
+            setData("/dubbo/config/appname", "The content from higer level 
node");
+            setData("/dubbo/config/appname/tagrouters", "The content from 
appname tagrouters");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+
+        configUrl = URL.valueOf("zookeeper://localhost:" + zkServerPort);
+
+        configuration = 
ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getExtension(configUrl.getProtocol());
+        configuration.initWith(configUrl);
+    }
+
+    @AfterClass
+    public static void tearDown() throws Exception {
+        zkServer.stop();
+    }
+
+    @Test
+    public void testGetConfig() throws Exception {
+        Assert.assertEquals("The content from dubbo.properties", 
configuration.getConfig("dubbo.dubbo.properties"));
+        Assert.assertEquals("The content from dubbo.properties", 
configuration.getConfig("dubbo.properties", "dubbo"));
+    }
+
+    @Test
+    public void testAddListener() throws Exception {
+        CountDownLatch latch = new CountDownLatch(4);
+        TestListener listener1 = new TestListener(latch);
+        TestListener listener2 = new TestListener(latch);
+        TestListener listener3 = new TestListener(latch);
+        TestListener listener4 = new TestListener(latch);
+        configuration.addListener("group*service:version.configurators", 
listener1);
+        configuration.addListener("group*service:version.configurators", 
listener2);
+        configuration.addListener("appname.tagrouters", listener3);
+        configuration.addListener("appname.tagrouters", listener4);
+
+        setData("/dubbo/config/group*service:version/configurators", "new 
value1");
+        Thread.sleep(100);
+        setData("/dubbo/config/appname/tagrouters", "new value2");
+        Thread.sleep(100);
+        setData("/dubbo/config/appname", "new value3");
+
+        Thread.sleep(5000);
+
+        latch.await();
+        Assert.assertEquals(1, 
listener1.getCount("group*service:version.configurators"));
+        Assert.assertEquals(1, 
listener2.getCount("group*service:version.configurators"));
+        Assert.assertEquals(1, listener3.getCount("appname.tagrouters"));
+        Assert.assertEquals(1, listener4.getCount("appname.tagrouters"));
+
+        Assert.assertEquals("new value1", listener1.getValue());
+        Assert.assertEquals("new value1", listener2.getValue());
+        Assert.assertEquals("new value2", listener3.getValue());
+        Assert.assertEquals("new value2", listener4.getValue());
+    }
+
+    private static void setData(String path, String data) throws Exception {
+        if (client.checkExists().forPath(path) == null) {
+            client.create().creatingParentsIfNeeded().forPath(path);
+        }
+        client.setData().forPath(path, data.getBytes());
+    }
+
+    private class TestListener implements ConfigurationListener {
+        private CountDownLatch latch;
+        private String value;
+        private Map<String, Integer> countMap = new HashMap<>();
+
+        public TestListener(CountDownLatch latch) {
+            this.latch = latch;
+        }
+
+        @Override
+        public void process(ConfigChangeEvent event) {
+            Integer count = countMap.computeIfAbsent(event.getKey(), k -> new 
Integer(0));
+            countMap.put(event.getKey(), ++count);
+
+            value = event.getNewValue();
+            latch.countDown();
+        }
+
+        public int getCount(String key) {
+            return countMap.get(key);
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+}
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml 
b/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
index 8e3d7fe..717f5ef 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
@@ -25,7 +25,7 @@
     <artifactId>dubbo-configcenter-zookeeper</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <description>The zookeeper implementation of the configcenter 
api</description>
+    <description>The zookeeper implementation of the config-center 
api</description>
 
     <dependencies>
         <dependency>
@@ -33,21 +33,31 @@
             <artifactId>dubbo-configcenter-api</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
-        <dependency>
-            <groupId>com.netflix.archaius</groupId>
-            <artifactId>archaius-core</artifactId>
-        </dependency>
+        <!--<dependency>-->
+        <!--<groupId>org.apache.curator</groupId>-->
+        <!--<artifactId>curator-framework</artifactId>-->
+        <!--</dependency>-->
         <dependency>
             <groupId>org.apache.curator</groupId>
-            <artifactId>curator-framework</artifactId>
+            <artifactId>curator-recipes</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.curator</groupId>
-            <artifactId>curator-recipes</artifactId>
+            <artifactId>curator-x-async</artifactId>
+            <version>4.0.1</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.zookeeper</groupId>
+                    <artifactId>zookeeper</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
+
         <dependency>
-            <groupId>commons-configuration</groupId>
-            <artifactId>commons-configuration</artifactId>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-test</artifactId>
+            <version>2.12.0</version>
+            <scope>test</scope>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git 
a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
 
b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
index b948be5..0379c0e 100644
--- 
a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
+++ 
b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
@@ -1,2 +1 @@
-archaius=org.apache.dubbo.configcenter.support.archaius.ArchaiusDynamicConfiguration
-zookeeper=org.apache.dubbo.configcenter.support.archaius.ArchaiusDynamicConfiguration
\ No newline at end of file
+zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfiguration
\ No newline at end of file
diff --git 
a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
 
b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
new file mode 100644
index 0000000..382e77d
--- /dev/null
+++ 
b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.dubbo.configcenter.support.zookeeper;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.test.TestingServer;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.configcenter.ConfigChangeEvent;
+import org.apache.dubbo.configcenter.ConfigurationListener;
+import org.apache.dubbo.configcenter.DynamicConfiguration;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ *
+ */
+public class ZookeeperDynamicConfigurationTest {
+    private static CuratorFramework client;
+
+    private static URL configUrl;
+    private static int zkServerPort = NetUtils.getAvailablePort();
+    private static TestingServer zkServer;
+    private static DynamicConfiguration configuration;
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        zkServer = new TestingServer(zkServerPort, true);
+
+        client = CuratorFrameworkFactory.newClient("localhost:" + 
zkServerPort, 60 * 1000, 60 * 1000,
+                new ExponentialBackoffRetry(1000, 3));
+        client.start();
+
+        try {
+            setData("/dubbo/config/dubbo/dubbo.properties", "The content from 
dubbo.properties");
+            setData("/dubbo/config/group*service:version/configurators", "The 
content from configurators");
+            setData("/dubbo/config/appname", "The content from higer level 
node");
+            setData("/dubbo/config/appname/tagrouters", "The content from 
appname tagrouters");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+
+        configUrl = URL.valueOf("zookeeper://localhost:" + zkServerPort);
+
+        configuration = 
ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getExtension(configUrl.getProtocol());
+        configuration.initWith(configUrl);
+    }
+
+    @AfterClass
+    public static void tearDown() throws Exception {
+        zkServer.stop();
+    }
+
+    @Test
+    public void testGetConfig() throws Exception {
+        Assert.assertEquals("The content from dubbo.properties", 
configuration.getConfig("dubbo.dubbo.properties"));
+        Assert.assertEquals("The content from dubbo.properties", 
configuration.getConfig("dubbo.properties", "dubbo"));
+    }
+
+    @Test
+    public void testAddListener() throws Exception {
+        CountDownLatch latch = new CountDownLatch(4);
+        TestListener listener1 = new TestListener(latch);
+        TestListener listener2 = new TestListener(latch);
+        TestListener listener3 = new TestListener(latch);
+        TestListener listener4 = new TestListener(latch);
+        configuration.addListener("group*service:version.configurators", 
listener1);
+        configuration.addListener("group*service:version.configurators", 
listener2);
+        configuration.addListener("appname.tagrouters", listener3);
+        configuration.addListener("appname.tagrouters", listener4);
+
+        setData("/dubbo/config/group*service:version/configurators", "new 
value1");
+        Thread.sleep(100);
+        setData("/dubbo/config/appname/tagrouters", "new value2");
+        Thread.sleep(100);
+        setData("/dubbo/config/appname", "new value3");
+
+        Thread.sleep(5000);
+
+        latch.await();
+        Assert.assertEquals(1, 
listener1.getCount("group*service:version.configurators"));
+        Assert.assertEquals(1, 
listener2.getCount("group*service:version.configurators"));
+        Assert.assertEquals(1, listener3.getCount("appname.tagrouters"));
+        Assert.assertEquals(1, listener4.getCount("appname.tagrouters"));
+
+        Assert.assertEquals("new value1", listener1.getValue());
+        Assert.assertEquals("new value1", listener2.getValue());
+        Assert.assertEquals("new value2", listener3.getValue());
+        Assert.assertEquals("new value2", listener4.getValue());
+    }
+
+    private static void setData(String path, String data) throws Exception {
+        if (client.checkExists().forPath(path) == null) {
+            client.create().creatingParentsIfNeeded().forPath(path);
+        }
+        client.setData().forPath(path, data.getBytes());
+    }
+
+    private class TestListener implements ConfigurationListener {
+        private CountDownLatch latch;
+        private String value;
+        private Map<String, Integer> countMap = new HashMap<>();
+
+        public TestListener(CountDownLatch latch) {
+            this.latch = latch;
+        }
+
+        @Override
+        public void process(ConfigChangeEvent event) {
+            Integer count = countMap.computeIfAbsent(event.getKey(), k -> new 
Integer(0));
+            countMap.put(event.getKey(), ++count);
+
+            value = event.getNewValue();
+            latch.countDown();
+        }
+
+        public int getCount(String key) {
+            return countMap.get(key);
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+}
diff --git a/dubbo-configcenter/pom.xml b/dubbo-configcenter/pom.xml
index fa1be8e..4d7dd84 100644
--- a/dubbo-configcenter/pom.xml
+++ b/dubbo-configcenter/pom.xml
@@ -25,14 +25,16 @@
     <artifactId>dubbo-configcenter</artifactId>
     <packaging>pom</packaging>
     <name>${project.artifactId}</name>
-    <description>The service configcenter module of the Dubbo 
project</description>
+    <description>The service config-center module of the Dubbo 
project</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
     </properties>
 
     <modules>
         <module>dubbo-configcenter-api</module>
-        <module>dubbo-configcenter-apollo</module>
         <module>dubbo-configcenter-zookeeper</module>
+        <!--<module>dubbo-configcenter-zookeeper-cache</module>-->
+        <module>dubbo-configcenter-apollo</module>
+        <module>dubbo-configcenter-archaius</module>
     </modules>
 </project>
\ No newline at end of file

Reply via email to