[ 
https://issues.apache.org/jira/browse/SCB-36?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16319759#comment-16319759
 ] 

ASF GitHub Bot commented on SCB-36:
-----------------------------------

liubao68 closed pull request #489: SCB-36 Support dynamic configuration for 
chassis by integrating with Apollo
URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/489
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/coverage-reports/pom.xml b/coverage-reports/pom.xml
index e7f950aa1..117ba26c8 100644
--- a/coverage-reports/pom.xml
+++ b/coverage-reports/pom.xml
@@ -167,6 +167,10 @@
       <groupId>io.servicecomb</groupId>
       <artifactId>handler-tracing-zipkin</artifactId>
     </dependency>
+    <dependency>
+      <groupId>io.servicecomb</groupId>
+      <artifactId>config-apollo</artifactId>
+    </dependency>
     <dependency>
       <groupId>io.servicecomb.demo</groupId>
       <artifactId>pojo-client</artifactId>
diff --git a/dynamic-config/config-apollo/pom.xml 
b/dynamic-config/config-apollo/pom.xml
new file mode 100644
index 000000000..1f082546f
--- /dev/null
+++ b/dynamic-config/config-apollo/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<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";>
+  <parent>
+    <artifactId>dynamic-config</artifactId>
+    <groupId>io.servicecomb</groupId>
+    <version>0.6.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>config-apollo</artifactId>
+  <dependencies>
+    <dependency>
+      <groupId>io.servicecomb</groupId>
+      <artifactId>foundation-config</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.servicecomb</groupId>
+      <artifactId>foundation-vertx</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-web</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>javax.validation</groupId>
+      <artifactId>validation-api</artifactId>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git 
a/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/archaius/sources/ApolloConfigurationSourceImpl.java
 
b/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/archaius/sources/ApolloConfigurationSourceImpl.java
new file mode 100644
index 000000000..b4d6b4ed3
--- /dev/null
+++ 
b/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/archaius/sources/ApolloConfigurationSourceImpl.java
@@ -0,0 +1,129 @@
+/*
+ * 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 io.servicecomb.config.archaius.sources;
+
+import static com.netflix.config.WatchedUpdateResult.createIncremental;
+import static io.servicecomb.config.client.ConfigurationAction.CREATE;
+import static io.servicecomb.config.client.ConfigurationAction.DELETE;
+import static io.servicecomb.config.client.ConfigurationAction.SET;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.validation.constraints.NotNull;
+
+import org.apache.commons.configuration.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+import com.netflix.config.WatchedUpdateListener;
+import com.netflix.config.WatchedUpdateResult;
+
+import io.servicecomb.config.ConfigMapping;
+import io.servicecomb.config.client.ApolloClient;
+import io.servicecomb.config.client.ApolloConfig;
+import io.servicecomb.config.client.ConfigurationAction;
+import io.servicecomb.config.spi.ConfigCenterConfigurationSource;
+
+public class ApolloConfigurationSourceImpl implements 
ConfigCenterConfigurationSource {
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(ConfigCenterConfigurationSource.class);
+
+  private final Map<String, Object> valueCache = new ConcurrentHashMap<>();
+
+  private List<WatchedUpdateListener> listeners = new CopyOnWriteArrayList<>();
+
+  public ApolloConfigurationSourceImpl() {
+  }
+
+  private final UpdateHandler updateHandler = new UpdateHandler();
+
+  @Override
+  public void init(Configuration localConfiguration) {
+    ApolloConfig.setConcurrentCompositeConfiguration(localConfiguration);
+    init();
+  }
+
+  private void init() {
+    ApolloClient apolloClient = new ApolloClient(updateHandler);
+    apolloClient.refreshApolloConfig();
+  }
+
+  @Override
+  public void addUpdateListener(@NotNull WatchedUpdateListener 
watchedUpdateListener) {
+    listeners.add(watchedUpdateListener);
+  }
+
+  @Override
+  public void removeUpdateListener(@NotNull WatchedUpdateListener 
watchedUpdateListener) {
+    listeners.remove(watchedUpdateListener);
+  }
+
+  private void updateConfiguration(WatchedUpdateResult result) {
+    for (WatchedUpdateListener l : listeners) {
+      try {
+        l.updateConfiguration(result);
+      } catch (Throwable ex) {
+        LOGGER.error("Error in invoking WatchedUpdateListener", ex);
+      }
+    }
+  }
+
+  @Override
+  public Map<String, Object> getCurrentData() throws Exception {
+    return valueCache;
+  }
+
+  public List<WatchedUpdateListener> getCurrentListeners() {
+    return listeners;
+  }
+
+  public class UpdateHandler {
+    public void handle(ConfigurationAction action, Map<String, Object> config) 
{
+      if (config == null || config.isEmpty()) {
+        return;
+      }
+      Map<String, Object> configuration = 
ConfigMapping.getConvertedMap(config);
+      if (CREATE.equals(action)) {
+        valueCache.putAll(configuration);
+
+        
updateConfiguration(createIncremental(ImmutableMap.copyOf(configuration),
+            null,
+            null));
+      } else if (SET.equals(action)) {
+        valueCache.putAll(configuration);
+
+        updateConfiguration(createIncremental(null, 
ImmutableMap.copyOf(configuration),
+            null));
+      } else if (DELETE.equals(action)) {
+        for (String itemKey : configuration.keySet()) {
+          valueCache.remove(itemKey);
+        }
+        updateConfiguration(createIncremental(null,
+            null,
+            ImmutableMap.copyOf(configuration)));
+      } else {
+        LOGGER.error("action: {} is invalid.", action.name());
+        return;
+      }
+      LOGGER.warn("Config value cache changed: action:{}; item:{}", 
action.name(), configuration.keySet());
+    }
+  }
+}
diff --git 
a/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/client/ApolloClient.java
 
b/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/client/ApolloClient.java
new file mode 100644
index 000000000..fcf70df59
--- /dev/null
+++ 
b/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/client/ApolloClient.java
@@ -0,0 +1,168 @@
+/*
+ * 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 io.servicecomb.config.client;
+
+import static io.servicecomb.config.client.ConfigurationAction.CREATE;
+import static io.servicecomb.config.client.ConfigurationAction.DELETE;
+import static io.servicecomb.config.client.ConfigurationAction.SET;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import io.netty.handler.codec.http.HttpResponseStatus;
+import 
io.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl.UpdateHandler;
+import io.servicecomb.foundation.common.utils.JsonUtils;
+
+public class ApolloClient {
+
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(ApolloClient.class);
+
+  private static final ApolloConfig APOLLO_CONFIG = ApolloConfig.INSTANCE;
+
+  private static final Map<String, Object> originalConfigMap = new 
ConcurrentHashMap<>();
+
+  private static final ScheduledExecutorService EXECUTOR = 
Executors.newScheduledThreadPool(1);
+
+  private final int refreshInterval = APOLLO_CONFIG.getRefreshInterval();
+
+  private final int firstRefreshInterval = 
APOLLO_CONFIG.getFirstRefreshInterval();
+
+  private final String serviceUri = APOLLO_CONFIG.getServerUri();
+
+  private final String serviceName = APOLLO_CONFIG.getServiceName();
+
+  private final String token = APOLLO_CONFIG.getToken();
+
+  private final String env = APOLLO_CONFIG.getEnv();
+
+  private final String clusters = APOLLO_CONFIG.getServerClusters();
+
+  private final String namespace = APOLLO_CONFIG.getNamespace();
+
+  private final UpdateHandler updateHandler;
+
+  private static RestTemplate rest = new RestTemplate();
+
+  public ApolloClient(UpdateHandler updateHandler) {
+    this.updateHandler = updateHandler;
+  }
+
+  public void refreshApolloConfig() {
+    EXECUTOR
+        .scheduleWithFixedDelay(new ConfigRefresh(serviceUri), 
firstRefreshInterval, refreshInterval, TimeUnit.SECONDS);
+  }
+
+  class ConfigRefresh implements Runnable {
+    private final String serviceUri;
+
+    ConfigRefresh(String serviceUris) {
+      this.serviceUri = serviceUris;
+    }
+
+    @Override
+    public void run() {
+      try {
+        refreshConfig();
+      } catch (Exception e) {
+
+        LOGGER.error("client refresh thread exception ", e);
+      }
+    }
+
+    void refreshConfig() {
+      HttpHeaders headers = new HttpHeaders();
+      headers.add("Content-Type", "application/json;charset=UTF-8");
+      headers.add("Authorization", token);
+      HttpEntity<String> entity = new HttpEntity<>(headers);
+      ResponseEntity<String> exchange = rest.exchange(composeAPI(), 
HttpMethod.GET, entity, String.class);
+      if (HttpResponseStatus.OK.code() == exchange.getStatusCode().value()) {
+        try {
+          Map<String, Object> body = 
JsonUtils.OBJ_MAPPER.readValue(exchange.getBody(),
+              new TypeReference<Map<String, Object>>() {
+              });
+          refreshConfigItems((Map<String, Object>) body.get("configurations"));
+        } catch (IOException e) {
+          LOGGER.error("JsonObject parse config center response error: ", e);
+        }
+      } else {
+        LOGGER.error("fetch configuration failed, error code:{} for {}", 
exchange.getStatusCodeValue(),
+            exchange.getBody());
+      }
+    }
+
+    private String composeAPI() {
+      String api = serviceUri + "/openapi/v1/envs/" + env +
+          "/apps/" + serviceName +
+          "/clusters/" + clusters +
+          "/namespaces/" + namespace +
+          "/releases/latest";
+      return api;
+    }
+
+    private void refreshConfigItems(Map<String, Object> map) {
+      compareChangedConfig(originalConfigMap, map);
+      originalConfigMap.clear();
+      originalConfigMap.putAll(map);
+    }
+
+    void compareChangedConfig(Map<String, Object> before, Map<String, Object> 
after) {
+      Map<String, Object> itemsCreated = new HashMap<>();
+      Map<String, Object> itemsDeleted = new HashMap<>();
+      Map<String, Object> itemsModified = new HashMap<>();
+      if (before == null || before.isEmpty()) {
+        updateHandler.handle(CREATE, after);
+        return;
+      }
+      if (after == null || after.isEmpty()) {
+        updateHandler.handle(DELETE, before);
+        return;
+      }
+      for (String itemKey : after.keySet()) {
+        if (!before.containsKey(itemKey)) {
+          itemsCreated.put(itemKey, after.get(itemKey));
+        } else if (!after.get(itemKey).equals(before.get(itemKey))) {
+          itemsModified.put(itemKey, after.get(itemKey));
+        }
+      }
+      for (String itemKey : before.keySet()) {
+        if (!after.containsKey(itemKey)) {
+          itemsDeleted.put(itemKey, "");
+        }
+      }
+      updateHandler.handle(CREATE, itemsCreated);
+      updateHandler.handle(SET, itemsModified);
+      updateHandler.handle(DELETE, itemsDeleted);
+    }
+  }
+}
+
diff --git 
a/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/client/ApolloConfig.java
 
b/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/client/ApolloConfig.java
new file mode 100644
index 000000000..b55b5db2c
--- /dev/null
+++ 
b/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/client/ApolloConfig.java
@@ -0,0 +1,89 @@
+/*
+ * 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 io.servicecomb.config.client;
+
+import org.apache.commons.configuration.Configuration;
+
+public class ApolloConfig {
+  public static final ApolloConfig INSTANCE = new ApolloConfig();
+
+  private static Configuration finalConfig;
+
+  private static final String SERVER_URL_KEY = "apollo.config.serverUri";
+
+  private static final String SERVER_NAMESPACE = "apollo.config.namespace";
+
+  private static final String SERVER_ENV = "apollo.config.env";
+
+  private static final String SERVER_CLUSTERS = "apollo.config.clusters";
+
+  private static final String APOLLO_SERVICE_NAME = 
"apollo.config.serviceName";
+
+  private static final String TOKEN = "apollo.config.token";
+
+  private static final String REFRESH_INTERVAL = 
"apollo.config.refreshInterval";
+
+  private static final String FIRST_REFRESH_INTERVAL = 
"apollo.config.firstRefreshInterval";
+
+  private static final int DEFAULT_REFRESH_INTERVAL = 3;
+
+  private static final int DEFAULT_FIRST_REFRESH_INTERVAL = 0;
+
+  private ApolloConfig() {
+  }
+
+  public static void setConcurrentCompositeConfiguration(Configuration config) 
{
+    finalConfig = config;
+  }
+
+  public Configuration getConcurrentCompositeConfiguration() {
+    return finalConfig;
+  }
+
+  public String getServiceName() {
+    return finalConfig.getString(APOLLO_SERVICE_NAME);
+  }
+
+  public String getServerUri() {
+    return finalConfig.getString(SERVER_URL_KEY);
+  }
+
+  public String getToken() {
+    return finalConfig.getString(TOKEN);
+  }
+
+  public String getEnv() {
+    return finalConfig.getString(SERVER_ENV);
+  }
+
+  public String getNamespace() {
+    return finalConfig.getString(SERVER_NAMESPACE);
+  }
+
+  public String getServerClusters() {
+    return finalConfig.getString(SERVER_CLUSTERS);
+  }
+
+  public int getRefreshInterval() {
+    return finalConfig.getInt(REFRESH_INTERVAL, DEFAULT_REFRESH_INTERVAL);
+  }
+
+  public int getFirstRefreshInterval() {
+    return finalConfig.getInt(FIRST_REFRESH_INTERVAL, 
DEFAULT_FIRST_REFRESH_INTERVAL);
+  }
+}
diff --git 
a/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/client/ConfigurationAction.java
 
b/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/client/ConfigurationAction.java
new file mode 100644
index 000000000..62552a147
--- /dev/null
+++ 
b/dynamic-config/config-apollo/src/main/java/io/servicecomb/config/client/ConfigurationAction.java
@@ -0,0 +1,24 @@
+/*
+ * 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 io.servicecomb.config.client;
+
+public enum ConfigurationAction {
+  CREATE,
+  SET,
+  DELETE
+}
diff --git 
a/dynamic-config/config-apollo/src/main/resources/META-INF/services/io.servicecomb.config.spi.ConfigCenterConfigurationSource
 
b/dynamic-config/config-apollo/src/main/resources/META-INF/services/io.servicecomb.config.spi.ConfigCenterConfigurationSource
new file mode 100644
index 000000000..d696ccd1d
--- /dev/null
+++ 
b/dynamic-config/config-apollo/src/main/resources/META-INF/services/io.servicecomb.config.spi.ConfigCenterConfigurationSource
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+io.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl
diff --git 
a/dynamic-config/config-apollo/src/test/java/io/servicecomb/config/archaius/sources/ApolloConfigurationSourceImplTest.java
 
b/dynamic-config/config-apollo/src/test/java/io/servicecomb/config/archaius/sources/ApolloConfigurationSourceImplTest.java
new file mode 100644
index 000000000..848cdd139
--- /dev/null
+++ 
b/dynamic-config/config-apollo/src/test/java/io/servicecomb/config/archaius/sources/ApolloConfigurationSourceImplTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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 io.servicecomb.config.archaius.sources;
+
+import static io.servicecomb.config.client.ConfigurationAction.CREATE;
+import static io.servicecomb.config.client.ConfigurationAction.DELETE;
+import static io.servicecomb.config.client.ConfigurationAction.SET;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import com.netflix.config.WatchedUpdateListener;
+
+import 
io.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl.UpdateHandler;
+import mockit.Deencapsulation;
+
+public class ApolloConfigurationSourceImplTest {
+  @Test
+  public void testCreate() throws Exception {
+
+    ApolloConfigurationSourceImpl apolloConfigurationSource = new 
ApolloConfigurationSourceImpl();
+    apolloConfigurationSource.addUpdateListener(result -> 
Assert.assertTrue(!result.getAdded().isEmpty()));
+    UpdateHandler udateHandler = 
Deencapsulation.getField(apolloConfigurationSource, UpdateHandler.class);
+    Map<String, Object> createItems = new HashMap<>();
+    createItems.put("testKey", "testValue");
+    udateHandler.handle(CREATE, createItems);
+  }
+
+  @Test
+  public void testUpdate() throws Exception {
+
+    ApolloConfigurationSourceImpl apolloConfigurationSource = new 
ApolloConfigurationSourceImpl();
+    apolloConfigurationSource.addUpdateListener(result -> 
Assert.assertTrue(!result.getChanged().isEmpty()));
+    UpdateHandler udateHandler = 
Deencapsulation.getField(apolloConfigurationSource, UpdateHandler.class);
+    Map<String, Object> updateItems = new HashMap<>();
+    updateItems.put("testKey", "testValue");
+    udateHandler.handle(SET, updateItems);
+  }
+
+  @Test
+  public void testDelete() throws Exception {
+    ApolloConfigurationSourceImpl apolloConfigurationSource = new 
ApolloConfigurationSourceImpl();
+    apolloConfigurationSource.addUpdateListener(result -> 
Assert.assertTrue(!result.getDeleted().isEmpty()));
+    UpdateHandler udateHandler = 
Deencapsulation.getField(apolloConfigurationSource, UpdateHandler.class);
+    Map<String, Object> deleteItems = new HashMap<>();
+    deleteItems.put("testKey", "testValue");
+
+    apolloConfigurationSource.getCurrentData().put("testKey", "testValue");
+    udateHandler.handle(DELETE, deleteItems);
+    Assert.assertTrue(apolloConfigurationSource.getCurrentData().isEmpty());
+  }
+
+  @Test
+  public void testRemoveUpdateListener() {
+    ApolloConfigurationSourceImpl apolloConfigurationSource = new 
ApolloConfigurationSourceImpl();
+    WatchedUpdateListener watchedUpdateListener = 
Mockito.mock(WatchedUpdateListener.class);
+    apolloConfigurationSource.addUpdateListener(watchedUpdateListener);
+    apolloConfigurationSource.removeUpdateListener(watchedUpdateListener);
+    
Assert.assertTrue(apolloConfigurationSource.getCurrentListeners().isEmpty());
+  }
+}
\ No newline at end of file
diff --git 
a/dynamic-config/config-apollo/src/test/java/io/servicecomb/config/client/ApolloClientTest.java
 
b/dynamic-config/config-apollo/src/test/java/io/servicecomb/config/client/ApolloClientTest.java
new file mode 100644
index 000000000..74a250d18
--- /dev/null
+++ 
b/dynamic-config/config-apollo/src/test/java/io/servicecomb/config/client/ApolloClientTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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 io.servicecomb.config.client;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+import io.servicecomb.config.ConfigUtil;
+import io.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl;
+import 
io.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl.UpdateHandler;
+import io.servicecomb.config.client.ApolloClient.ConfigRefresh;
+import mockit.Deencapsulation;
+
+public class ApolloClientTest {
+  @BeforeClass
+  public static void setUpClass() {
+
+    
ApolloConfig.setConcurrentCompositeConfiguration(ConfigUtil.createLocalConfig());
+  }
+
+  @Test
+  public void refreshApolloConfig() {
+    ApolloConfig apolloConfig = ApolloConfig.INSTANCE;
+    RestTemplate rest = Mockito.mock(RestTemplate.class);
+
+    ResponseEntity<String> responseEntity = new ResponseEntity<>(
+        "{\"apollo\":\"mocked\", \"configurations\":{\"timeout\":1000}}", 
HttpStatus.OK);
+    Mockito.when(rest.exchange(
+        Matchers.anyString(),
+        Matchers.any(HttpMethod.class),
+        Matchers.<HttpEntity<String>>any(),
+        Matchers.<Class<String>>any()
+        )
+    ).thenReturn(responseEntity);
+    ApolloConfigurationSourceImpl impl = new ApolloConfigurationSourceImpl();
+    UpdateHandler updateHandler = impl.new UpdateHandler();
+    ApolloClient apolloClient = new ApolloClient(updateHandler);
+    Deencapsulation.setField(apolloClient, "rest", rest);
+    ConfigRefresh cr = apolloClient.new 
ConfigRefresh(apolloConfig.getServerUri());
+    cr.run();
+
+    Map<String, Object> originMap = Deencapsulation.getField(apolloClient, 
"originalConfigMap");
+    Assert.assertEquals(1, originMap.size());
+  }
+
+  @Test
+  public void testCompareChangedConfig() {
+    boolean status = true;
+    Map<String, Object> before = new HashMap<>();
+    Map<String, Object> after = new HashMap<>();
+
+    ApolloConfigurationSourceImpl impl = new ApolloConfigurationSourceImpl();
+    UpdateHandler updateHandler = impl.new UpdateHandler();
+    ApolloClient apolloClient = new ApolloClient(updateHandler);
+
+    ConfigRefresh cr = apolloClient.new ConfigRefresh("");
+
+    ApolloConfig instance = ApolloConfig.INSTANCE;
+    try {
+      Deencapsulation.invoke(cr, "compareChangedConfig", before, after);
+    } catch (Exception e) {
+      status = false;
+    }
+    Assert.assertTrue(status);
+
+    status = true;
+    before.put("test", "testValue");
+    try {
+      Deencapsulation.invoke(cr, "compareChangedConfig", before, after);
+    } catch (Exception e) {
+      status = false;
+    }
+    Assert.assertTrue(status);
+
+    status = true;
+    after.put("test", "testValue2");
+    try {
+      Deencapsulation.invoke(cr, "compareChangedConfig", before, after);
+    } catch (Exception e) {
+      status = false;
+    }
+    Assert.assertTrue(status);
+
+    status = true;
+    try {
+      Deencapsulation.invoke(cr, "compareChangedConfig", before, after);
+    } catch (Exception e) {
+      status = false;
+    }
+    Assert.assertTrue(status);
+  }
+}
\ No newline at end of file
diff --git 
a/dynamic-config/config-apollo/src/test/java/io/servicecomb/config/client/ApolloConfigTest.java
 
b/dynamic-config/config-apollo/src/test/java/io/servicecomb/config/client/ApolloConfigTest.java
new file mode 100644
index 000000000..e343cc98b
--- /dev/null
+++ 
b/dynamic-config/config-apollo/src/test/java/io/servicecomb/config/client/ApolloConfigTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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 io.servicecomb.config.client;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import io.servicecomb.config.ConfigUtil;
+
+public class ApolloConfigTest {
+  @BeforeClass
+  public static void setUpClass() {
+    
ApolloConfig.setConcurrentCompositeConfiguration(ConfigUtil.createLocalConfig());
+  }
+
+  @Test
+  public void getServiceName() {
+    ApolloConfig instance = ApolloConfig.INSTANCE;
+    Assert.assertEquals("apollo-test", instance.getServiceName());
+    Assert.assertEquals("http://127.0.0.1:8070";, instance.getServerUri());
+    Assert.assertEquals("DEV", instance.getEnv());
+    Assert.assertEquals("test-cluster", instance.getServerClusters());
+    Assert.assertEquals("application", instance.getNamespace());
+    Assert.assertEquals("xxx", instance.getToken());
+    Assert.assertEquals(30, instance.getRefreshInterval());
+    Assert.assertEquals(0, instance.getFirstRefreshInterval());
+  }
+}
\ No newline at end of file
diff --git a/dynamic-config/config-apollo/src/test/resources/microservice.yaml 
b/dynamic-config/config-apollo/src/test/resources/microservice.yaml
new file mode 100644
index 000000000..f050218a5
--- /dev/null
+++ b/dynamic-config/config-apollo/src/test/resources/microservice.yaml
@@ -0,0 +1,54 @@
+#
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+APPLICATION_ID: pojotest
+service_description:
+  name: apollo-test
+  version: 1.0.1
+
+apollo:
+  config:
+    serverUri: http://127.0.0.1:8070
+    serviceName: apollo-test
+    env: DEV
+    clusters: test-cluster
+    namespace: application
+    token: xxx
+    refreshInterval: 30
+    firstRefreshInterval: 0
+
+cse:
+  config:
+    client:
+      serverUri: http://127.0.0.1:8070
+  service:
+    registry:
+      address: http://127.0.0.1:30100
+  rest:
+    address: 0.0.0.0:8080
+  highway:
+    address: 0.0.0.0:7070
+  handler:
+    chain:
+      Provider:
+        default: bizkeeper-provider
+  #executors:
+    #default: test
+    #Provider:
+      #server: test
+      #server.wrapParam: test
\ No newline at end of file
diff --git a/dynamic-config/pom.xml b/dynamic-config/pom.xml
new file mode 100644
index 000000000..a6ed80461
--- /dev/null
+++ b/dynamic-config/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<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";>
+  <parent>
+    <groupId>io.servicecomb</groupId>
+    <artifactId>java-chassis-parent</artifactId>
+    <version>0.6.0-SNAPSHOT</version>
+    <relativePath>../parent</relativePath>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>dynamic-config</artifactId>
+  <packaging>pom</packaging>
+  <modules>
+    <module>config-apollo</module>
+  </modules>
+
+</project>
\ No newline at end of file
diff --git a/java-chassis-dependencies/pom.xml 
b/java-chassis-dependencies/pom.xml
index a111a3ff1..86a3b2d2f 100644
--- a/java-chassis-dependencies/pom.xml
+++ b/java-chassis-dependencies/pom.xml
@@ -735,6 +735,11 @@
        <artifactId>foundation-config-cc</artifactId>
        <version>0.6.0-SNAPSHOT</version>
        </dependency>
+      <dependency>
+        <groupId>io.servicecomb</groupId>
+        <artifactId>config-apollo</artifactId>
+        <version>0.6.0-SNAPSHOT</version>
+      </dependency>
        <dependency>
        <groupId>io.servicecomb</groupId>
         <artifactId>foundation-vertx</artifactId>
diff --git a/pom.xml b/pom.xml
index d91a410e5..b1dfa7fea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,7 @@
     <module>tracing</module>
     <module>edge</module>
     <module>metrics</module>
+    <module>dynamic-config</module>
   </modules>
 
   <distributionManagement>
diff --git a/samples/config-apollo-sample/pom.xml 
b/samples/config-apollo-sample/pom.xml
new file mode 100644
index 000000000..e08aceb4b
--- /dev/null
+++ b/samples/config-apollo-sample/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<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";>
+  <parent>
+    <artifactId>samples</artifactId>
+    <groupId>io.servicecomb.samples</groupId>
+    <version>0.6.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>config-apollo-sample</artifactId>
+  <dependencies>
+    <dependency>
+      <groupId>io.servicecomb</groupId>
+      <artifactId>foundation-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.netflix.archaius</groupId>
+      <artifactId>archaius-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.servicecomb</groupId>
+      <artifactId>config-apollo</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+    </dependency>
+  </dependencies>
+
+
+</project>
\ No newline at end of file
diff --git a/samples/config-apollo-sample/src/main/java/MainServer.java 
b/samples/config-apollo-sample/src/main/java/MainServer.java
new file mode 100644
index 000000000..9a4d2b09e
--- /dev/null
+++ b/samples/config-apollo-sample/src/main/java/MainServer.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+import com.netflix.config.DynamicPropertyFactory;
+
+import io.servicecomb.foundation.common.utils.BeanUtils;
+import io.servicecomb.foundation.common.utils.Log4jUtils;
+
+public class MainServer {
+  public static void main(String[] args) throws Exception {
+    Log4jUtils.init();
+    BeanUtils.init();
+    while (true) {
+      
System.out.println(DynamicPropertyFactory.getInstance().getStringProperty("timeout",
 "default"));
+      Thread.sleep(3000);
+    }
+  }
+}
diff --git a/samples/config-apollo-sample/src/main/resources/log4j.properties 
b/samples/config-apollo-sample/src/main/resources/log4j.properties
new file mode 100644
index 000000000..ba2165551
--- /dev/null
+++ b/samples/config-apollo-sample/src/main/resources/log4j.properties
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+log4j.rootLogger=INFO
\ No newline at end of file
diff --git a/samples/config-apollo-sample/src/main/resources/microservice.yaml 
b/samples/config-apollo-sample/src/main/resources/microservice.yaml
new file mode 100644
index 000000000..fe4b1f18a
--- /dev/null
+++ b/samples/config-apollo-sample/src/main/resources/microservice.yaml
@@ -0,0 +1,51 @@
+#
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+APPLICATION_ID: pojotest
+service_description:
+  name: apollo-test
+  version: 1.0.1
+
+apollo:
+  config:
+    serverUri: http://127.0.0.1:8070
+    serviceName: apollo-test
+    env: DEV
+    clusters: test-cluster
+    namespace: application
+    token: #get token from Apollo web pages
+cse:
+  config:
+    client:
+      serverUri: http://127.0.0.1:8070
+  service:
+    registry:
+      address: http://127.0.0.1:30100
+  rest:
+    address: 0.0.0.0:8080
+  highway:
+    address: 0.0.0.0:7070
+  handler:
+    chain:
+      Provider:
+        default: bizkeeper-provider
+  #executors:
+    #default: test
+    #Provider:
+      #server: test
+      #server.wrapParam: test
\ No newline at end of file
diff --git a/samples/pom.xml b/samples/pom.xml
index d83bf3950..1d735dd03 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -34,6 +34,7 @@
     <module>auth-sample</module>
     <module>bmi</module>
     <module>metrics-write-file-sample</module>
+    <module>config-apollo-sample</module>
   </modules>
 
   <dependencyManagement>


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


> Support connecting to the third party dynamic config center
> -----------------------------------------------------------
>
>                 Key: SCB-36
>                 URL: https://issues.apache.org/jira/browse/SCB-36
>             Project: Apache ServiceComb
>          Issue Type: Sub-task
>          Components: Java-Chassis
>            Reporter: mabin
>            Assignee: Bo Li
>             Fix For: java-chassis-1.0.0-m1
>
>         Attachments: Dynamic Config Proposal.pdf
>
>
> Java-chassis can connect to another open source config center to support 
> changing the setting dynamically.



--
This message was sent by Atlassian JIRA
(v6.4.14#64029)

Reply via email to