You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by wu...@apache.org on 2018/06/04 02:03:00 UTC

[incubator-servicecomb-java-chassis] branch master updated (d2af11f -> ddacc17)

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

wujimin pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git.


    from d2af11f  [SCB-601]rename method
     new 2916139  [SCB-617] ConfigCenterConfigurationSource support uninit
     new ac505e4  [SCB-617] uninit config center source when shutdown
     new 38f6c46  [SCB-617] change SCBEngine.uninit to SCBEngine.destroy
     new fd4acf1  [SCB-617] shutdown producer executors
     new 22faf9d  [SCB-617] avoid MetricsBootstrap shutdown NPE
     new ddacc17  [SCB-617] avoid retry thread pool block shutdown

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../servicecomb/core/CseApplicationListener.java   |  3 +
 .../org/apache/servicecomb/core/SCBEngine.java     | 16 ++--
 .../core/executor/FixedThreadExecutor.java         | 16 +++-
 .../provider/producer/ProducerProviderManager.java | 37 ++++++++-
 .../org/apache/servicecomb/core/TestSCBEngine.java | 15 +++-
 .../core/TestCseApplicationListener.java           | 43 +++++-----
 .../producer/TestProducerProviderManager.java      | 91 ++++++++++++++++++++++
 .../ConfigCenterConfigurationSourceImpl.java       | 13 +++-
 .../config/client/ConfigCenterClient.java          | 32 +++++---
 .../apache/servicecomb/config/client/URIConst.java | 40 +++-------
 .../TestConfigCenterConfigurationSource.java       | 30 +++++++
 .../config/client/TestConfigCenterClient.java      | 51 ++++++++++--
 .../servicecomb/config/client/TestURIConst.java    | 26 ++++++-
 .../org/apache/servicecomb/config/ConfigUtil.java  | 10 +++
 .../spi/ConfigCenterConfigurationSource.java       |  4 +
 .../apache/servicecomb/config/TestConfigUtil.java  | 25 ++++++
 .../foundation/metrics/MetricsBootstrap.java       |  6 +-
 .../foundation/metrics/MetricsInitializer.java     |  2 +-
 .../foundation/metrics/TestMetricsBootstrap.java   | 20 +++--
 .../loadbalance/LoadbalanceHandler.java            |  5 +-
 .../loadbalance/TestLoadbalanceHandler.java        | 16 ++++
 .../tests/SpringMvcSpringIntegrationTest.java      |  2 +-
 .../tests/RawSpringMvcIntegrationTest.java         |  2 +-
 ...SimplifiedMappingAnnotationIntegrationTest.java |  2 +-
 ...SimplifiedMappingAnnotationIntegrationTest.java |  2 +-
 .../metrics/core/DefaultRegistryInitializer.java   |  2 +-
 .../core/TestDefaultRegistryInitializer.java       |  2 +-
 .../metrics/prometheus/PrometheusPublisher.java    |  2 +-
 .../prometheus/TestPrometheusPublisher.java        |  2 +-
 29 files changed, 412 insertions(+), 105 deletions(-)
 copy foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/TestClientVerticle.java => core/src/test/java/org/apache/servicecomb/core/org/apache/servicecomb/core/TestCseApplicationListener.java (54%)

-- 
To stop receiving notification emails like this one, please contact
wujimin@apache.org.

[incubator-servicecomb-java-chassis] 03/06: [SCB-617] change SCBEngine.uninit to SCBEngine.destroy

Posted by wu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit 38f6c4699707f19e156dd3e494f77f3eee58016b
Author: wujimin <wu...@huawei.com>
AuthorDate: Fri Jun 1 02:22:11 2018 +0800

    [SCB-617] change SCBEngine.uninit to SCBEngine.destroy
---
 .../org/apache/servicecomb/core/CseApplicationListener.java    |  2 +-
 core/src/main/java/org/apache/servicecomb/core/SCBEngine.java  | 10 +++++-----
 .../test/java/org/apache/servicecomb/core/TestSCBEngine.java   |  2 +-
 .../apache/servicecomb/core/TestCseApplicationListener.java    |  2 +-
 .../demo/springmvc/tests/SpringMvcSpringIntegrationTest.java   |  2 +-
 .../demo/springmvc/tests/RawSpringMvcIntegrationTest.java      |  2 +-
 ...ingMvcSpringSimplifiedMappingAnnotationIntegrationTest.java |  2 +-
 ...RawSpringMvcSimplifiedMappingAnnotationIntegrationTest.java |  2 +-
 8 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java b/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java
index 28b6e73..38793cd 100644
--- a/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java
+++ b/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java
@@ -73,7 +73,7 @@ public class CseApplicationListener
 
       SCBEngine.getInstance().init();
     } else if (event instanceof ContextClosedEvent) {
-      SCBEngine.getInstance().uninit();
+      SCBEngine.getInstance().destroy();
     }
   }
 }
diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java
index 0616c25..4d2f157 100644
--- a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java
+++ b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java
@@ -178,7 +178,7 @@ public class SCBEngine {
         doInit();
         status = SCBStatus.UP;
       } catch (Exception e) {
-        uninit();
+        destroy();
         status = SCBStatus.FAILED;
         throw new IllegalStateException("ServiceComb init failed.", e);
       }
@@ -218,23 +218,23 @@ public class SCBEngine {
 
     RegistryUtils.run();
 
-    Runtime.getRuntime().addShutdownHook(new Thread(this::uninit));
+    Runtime.getRuntime().addShutdownHook(new Thread(this::destroy));
   }
 
   /**
    * not allow throw any exception
    * even some step throw exception, must catch it and go on, otherwise shutdown process will be broken.
    */
-  public synchronized void uninit() {
+  public synchronized void destroy() {
     if (SCBStatus.UP.equals(status)) {
       LOGGER.info("ServiceComb is closing now...");
-      doUninit();
+      doDestroy();
       status = SCBStatus.DOWN;
       LOGGER.info("ServiceComb had closed");
     }
   }
 
-  private void doUninit() {
+  private void doDestroy() {
     //Step 1: notify all component stop invoke via BEFORE_CLOSE Event
     safeTriggerEvent(EventType.BEFORE_CLOSE);
 
diff --git a/core/src/test/java/org/apache/servicecomb/core/TestSCBEngine.java b/core/src/test/java/org/apache/servicecomb/core/TestSCBEngine.java
index f39636a..6b72f56 100644
--- a/core/src/test/java/org/apache/servicecomb/core/TestSCBEngine.java
+++ b/core/src/test/java/org/apache/servicecomb/core/TestSCBEngine.java
@@ -82,7 +82,7 @@ public class TestSCBEngine {
 
     Assert.assertEquals(SCBStatus.UP, engine.getStatus());
 
-    engine.uninit();
+    engine.destroy();
 
     Assert.assertEquals(SCBStatus.DOWN, engine.getStatus());
     Assert.assertTrue(configDestroy.get());
diff --git a/core/src/test/java/org/apache/servicecomb/core/org/apache/servicecomb/core/TestCseApplicationListener.java b/core/src/test/java/org/apache/servicecomb/core/org/apache/servicecomb/core/TestCseApplicationListener.java
index 26d7d78..aeac7e1 100644
--- a/core/src/test/java/org/apache/servicecomb/core/org/apache/servicecomb/core/TestCseApplicationListener.java
+++ b/core/src/test/java/org/apache/servicecomb/core/org/apache/servicecomb/core/TestCseApplicationListener.java
@@ -35,7 +35,7 @@ public class TestCseApplicationListener {
     AtomicInteger count = new AtomicInteger();
     SCBEngine scbEngine = new SCBEngine() {
       @Override
-      public synchronized void uninit() {
+      public synchronized void destroy() {
         count.incrementAndGet();
       }
     };
diff --git a/integration-tests/springmvc-tests/springmvc-tests-general-with-springboot/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcSpringIntegrationTest.java b/integration-tests/springmvc-tests/springmvc-tests-general-with-springboot/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcSpringIntegrationTest.java
index fbbf181..c240f61 100644
--- a/integration-tests/springmvc-tests/springmvc-tests-general-with-springboot/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcSpringIntegrationTest.java
+++ b/integration-tests/springmvc-tests/springmvc-tests-general-with-springboot/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcSpringIntegrationTest.java
@@ -35,6 +35,6 @@ public class SpringMvcSpringIntegrationTest extends SpringMvcIntegrationTestBase
 
   @AfterClass
   public static void shutdown() {
-    SCBEngine.getInstance().uninit();
+    SCBEngine.getInstance().destroy();
   }
 }
diff --git a/integration-tests/springmvc-tests/springmvc-tests-general/src/test/java/org/apache/servicecomb/demo/springmvc/tests/RawSpringMvcIntegrationTest.java b/integration-tests/springmvc-tests/springmvc-tests-general/src/test/java/org/apache/servicecomb/demo/springmvc/tests/RawSpringMvcIntegrationTest.java
index 7a49b20..adb09ea 100644
--- a/integration-tests/springmvc-tests/springmvc-tests-general/src/test/java/org/apache/servicecomb/demo/springmvc/tests/RawSpringMvcIntegrationTest.java
+++ b/integration-tests/springmvc-tests/springmvc-tests-general/src/test/java/org/apache/servicecomb/demo/springmvc/tests/RawSpringMvcIntegrationTest.java
@@ -32,6 +32,6 @@ public class RawSpringMvcIntegrationTest extends SpringMvcIntegrationTestBase {
 
   @AfterClass
   public static void shutdown() {
-    SCBEngine.getInstance().uninit();
+    SCBEngine.getInstance().destroy();
   }
 }
diff --git a/integration-tests/springmvc-tests/springmvc-tests-simplified-mapping-with-springboot/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcSpringSimplifiedMappingAnnotationIntegrationTest.java b/integration-tests/springmvc-tests/springmvc-tests-simplified-mapping-with-springboot/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcSpringSimplifiedMappingAnnotationIntegrationTest.java
index 292f926..bf6f709 100644
--- a/integration-tests/springmvc-tests/springmvc-tests-simplified-mapping-with-springboot/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcSpringSimplifiedMappingAnnotationIntegrationTest.java
+++ b/integration-tests/springmvc-tests/springmvc-tests-simplified-mapping-with-springboot/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcSpringSimplifiedMappingAnnotationIntegrationTest.java
@@ -36,6 +36,6 @@ public class SpringMvcSpringSimplifiedMappingAnnotationIntegrationTest extends S
 
   @AfterClass
   public static void shutdown() {
-    SCBEngine.getInstance().uninit();
+    SCBEngine.getInstance().destroy();
   }
 }
diff --git a/integration-tests/springmvc-tests/springmvc-tests-simplified-mapping/src/test/java/org/apache/servicecomb/demo/springmvc/tests/RawSpringMvcSimplifiedMappingAnnotationIntegrationTest.java b/integration-tests/springmvc-tests/springmvc-tests-simplified-mapping/src/test/java/org/apache/servicecomb/demo/springmvc/tests/RawSpringMvcSimplifiedMappingAnnotationIntegrationTest.java
index 35cdbd8..e836362 100644
--- a/integration-tests/springmvc-tests/springmvc-tests-simplified-mapping/src/test/java/org/apache/servicecomb/demo/springmvc/tests/RawSpringMvcSimplifiedMappingAnnotationIntegrationTest.java
+++ b/integration-tests/springmvc-tests/springmvc-tests-simplified-mapping/src/test/java/org/apache/servicecomb/demo/springmvc/tests/RawSpringMvcSimplifiedMappingAnnotationIntegrationTest.java
@@ -33,6 +33,6 @@ public class RawSpringMvcSimplifiedMappingAnnotationIntegrationTest extends Spri
 
   @AfterClass
   public static void shutdown() {
-    SCBEngine.getInstance().uninit();
+    SCBEngine.getInstance().destroy();
   }
 }

-- 
To stop receiving notification emails like this one, please contact
wujimin@apache.org.

[incubator-servicecomb-java-chassis] 04/06: [SCB-617] shutdown producer executors

Posted by wu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit fd4acf1b421062b17189b4c6d383316f7eb154f9
Author: wujimin <wu...@huawei.com>
AuthorDate: Mon May 28 16:27:57 2018 +0800

    [SCB-617] shutdown producer executors
---
 .../core/executor/FixedThreadExecutor.java         | 16 +++-
 .../provider/producer/ProducerProviderManager.java | 37 ++++++++-
 .../producer/TestProducerProviderManager.java      | 91 ++++++++++++++++++++++
 3 files changed, 139 insertions(+), 5 deletions(-)

diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/FixedThreadExecutor.java b/core/src/main/java/org/apache/servicecomb/core/executor/FixedThreadExecutor.java
index 2600ebb..8ea95df 100644
--- a/core/src/main/java/org/apache/servicecomb/core/executor/FixedThreadExecutor.java
+++ b/core/src/main/java/org/apache/servicecomb/core/executor/FixedThreadExecutor.java
@@ -17,10 +17,12 @@
 
 package org.apache.servicecomb.core.executor;
 
+import java.io.Closeable;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -30,7 +32,7 @@ import org.slf4j.LoggerFactory;
 
 import com.netflix.config.DynamicPropertyFactory;
 
-public class FixedThreadExecutor implements Executor {
+public class FixedThreadExecutor implements Executor, Closeable {
   private static final Logger LOGGER = LoggerFactory.getLogger(FixedThreadExecutor.class);
 
   public static final String KEY_GROUP = "servicecomb.executor.default.group";
@@ -38,7 +40,7 @@ public class FixedThreadExecutor implements Executor {
   public static final String KEY_THREAD = "servicecomb.executor.default.thread-per-group";
 
   // to avoid multiple network thread conflicted when put tasks to executor queue
-  private List<Executor> executorList = new ArrayList<>();
+  private List<ExecutorService> executorList = new ArrayList<>();
 
   // for bind network thread to one executor
   // it's impossible that has too many network thread, so index will not too big that less than 0
@@ -58,7 +60,7 @@ public class FixedThreadExecutor implements Executor {
     }
   }
 
-  public List<Executor> getExecutorList() {
+  public List<ExecutorService> getExecutorList() {
     return executorList;
   }
 
@@ -74,4 +76,12 @@ public class FixedThreadExecutor implements Executor {
     int idx = index.getAndIncrement() % executorList.size();
     return executorList.get(idx);
   }
+
+  @Override
+  public void close() {
+    for (ExecutorService executorService : executorList) {
+      executorService.shutdown();
+    }
+    executorList.clear();
+  }
 }
diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java
index 3595296..b9b6b41 100644
--- a/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java
+++ b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java
@@ -17,39 +17,72 @@
 
 package org.apache.servicecomb.core.provider.producer;
 
+import java.io.Closeable;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.ExecutorService;
 
 import javax.inject.Inject;
 
+import org.apache.commons.io.IOUtils;
+import org.apache.servicecomb.core.BootListener;
 import org.apache.servicecomb.core.ProducerProvider;
 import org.apache.servicecomb.core.definition.MicroserviceMeta;
 import org.apache.servicecomb.core.definition.MicroserviceMetaManager;
+import org.apache.servicecomb.core.definition.OperationMeta;
 import org.apache.servicecomb.core.definition.SchemaMeta;
 import org.apache.servicecomb.core.definition.SchemaUtils;
 import org.apache.servicecomb.serviceregistry.RegistryUtils;
 import org.apache.servicecomb.serviceregistry.api.registry.Microservice;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 @Component
-public class ProducerProviderManager {
+public class ProducerProviderManager implements BootListener {
+  private static final Logger LOGGER = LoggerFactory.getLogger(ProducerProviderManager.class);
+
   @Autowired(required = false)
   private List<ProducerProvider> producerProviderList = Collections.emptyList();
 
   @Inject
   private MicroserviceMetaManager microserviceMetaManager;
 
+  private MicroserviceMeta microserviceMeta;
+
   public void init() throws Exception {
     for (ProducerProvider provider : producerProviderList) {
       provider.init();
     }
 
     Microservice microservice = RegistryUtils.getMicroservice();
-    MicroserviceMeta microserviceMeta = microserviceMetaManager.getOrCreateMicroserviceMeta(microservice);
+    microserviceMeta = microserviceMetaManager.getOrCreateMicroserviceMeta(microservice);
     for (SchemaMeta schemaMeta : microserviceMeta.getSchemaMetas()) {
       String content = SchemaUtils.swaggerToString(schemaMeta.getSwagger());
       microservice.addSchema(schemaMeta.getSchemaId(), content);
     }
   }
+
+  @Override
+  public void onBootEvent(BootEvent event) {
+    if (!EventType.AFTER_CLOSE.equals(event.getEventType())) {
+      return;
+    }
+
+    for (OperationMeta operationMeta : microserviceMeta.getOperations()) {
+      if (ExecutorService.class.isInstance(operationMeta.getExecutor())) {
+        ((ExecutorService) operationMeta.getExecutor()).shutdown();
+        continue;
+      }
+
+      if (Closeable.class.isInstance(operationMeta.getExecutor())) {
+        IOUtils.closeQuietly((Closeable) operationMeta.getExecutor());
+        continue;
+      }
+
+      LOGGER.warn("Executor {} do not support close or shutdown, it may block service shutdown.",
+          operationMeta.getExecutor().getClass().getName());
+    }
+  }
 }
diff --git a/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.java b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.java
index 79c0832..f082240 100644
--- a/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.java
+++ b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.java
@@ -17,10 +17,28 @@
 
 package org.apache.servicecomb.core.provider.producer;
 
+import java.io.Closeable;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.servicecomb.core.BootListener.BootEvent;
+import org.apache.servicecomb.core.BootListener.EventType;
+import org.apache.servicecomb.core.definition.MicroserviceMeta;
 import org.apache.servicecomb.core.definition.MicroserviceMetaManager;
+import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.core.executor.FixedThreadExecutor;
+import org.apache.servicecomb.core.executor.ReactiveExecutor;
+import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector;
+import org.junit.Assert;
 import org.junit.Test;
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
+import mockit.Deencapsulation;
+import mockit.Expectations;
+import mockit.Mock;
+import mockit.MockUp;
 import mockit.Mocked;
 
 public class TestProducerProviderManager {
@@ -34,4 +52,77 @@ public class TestProducerProviderManager {
 
     context.close();
   }
+
+  @Test
+  public void onBootEvent_notClose() {
+    BootEvent event = new BootEvent();
+    event.setEventType(EventType.BEFORE_CLOSE);
+
+    ProducerProviderManager producerProviderManager = new ProducerProviderManager();
+    // should not throw exception
+    producerProviderManager.onBootEvent(event);
+  }
+
+  @Test
+  public void onBootEvent_close(@Mocked MicroserviceMeta microserviceMeta, @Mocked OperationMeta op1,
+      @Mocked OperationMeta op2, @Mocked FixedThreadExecutor closeable) {
+    AtomicInteger count = new AtomicInteger();
+    ExecutorService executorService = new MockUp<ExecutorService>() {
+      @Mock
+      void shutdown() {
+        count.incrementAndGet();
+      }
+    }.getMockInstance();
+    new MockUp<Closeable>(closeable) {
+      @Mock
+      void close() {
+        count.incrementAndGet();
+      }
+    };
+    new Expectations() {
+      {
+        microserviceMeta.getOperations();
+        result = Arrays.asList(op1, op2);
+        op1.getExecutor();
+        result = executorService;
+        op2.getExecutor();
+        result = closeable;
+      }
+    };
+    BootEvent event = new BootEvent();
+    event.setEventType(EventType.AFTER_CLOSE);
+
+    ProducerProviderManager producerProviderManager = new ProducerProviderManager();
+    Deencapsulation.setField(producerProviderManager, "microserviceMeta", microserviceMeta);
+
+    producerProviderManager.onBootEvent(event);
+
+    Assert.assertEquals(2, count.get());
+  }
+
+  @Test
+  public void onBootEvent_close_unknown(@Mocked MicroserviceMeta microserviceMeta, @Mocked OperationMeta op1) {
+    Executor executor = new ReactiveExecutor();
+    new Expectations() {
+      {
+        microserviceMeta.getOperations();
+        result = Arrays.asList(op1);
+        op1.getExecutor();
+        result = executor;
+      }
+    };
+    LogCollector logCollector = new LogCollector();
+    BootEvent event = new BootEvent();
+    event.setEventType(EventType.AFTER_CLOSE);
+
+    ProducerProviderManager producerProviderManager = new ProducerProviderManager();
+    Deencapsulation.setField(producerProviderManager, "microserviceMeta", microserviceMeta);
+
+    producerProviderManager.onBootEvent(event);
+
+    Assert.assertEquals(
+        "Executor org.apache.servicecomb.core.executor.ReactiveExecutor do not support close or shutdown, it may block service shutdown.",
+        logCollector.getEvents().get(0).getMessage());
+    logCollector.teardown();
+  }
 }

-- 
To stop receiving notification emails like this one, please contact
wujimin@apache.org.

[incubator-servicecomb-java-chassis] 06/06: [SCB-617] avoid retry thread pool block shutdown

Posted by wu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit ddacc1714817f0a0deb2e2915e71f980285a1212
Author: wujimin <wu...@huawei.com>
AuthorDate: Mon May 28 16:37:03 2018 +0800

    [SCB-617] avoid retry thread pool block shutdown
---
 .../servicecomb/loadbalance/LoadbalanceHandler.java      |  5 ++++-
 .../servicecomb/loadbalance/TestLoadbalanceHandler.java  | 16 ++++++++++++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java
index bcbbfd4..723abef 100644
--- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java
+++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java
@@ -68,7 +68,10 @@ public class LoadbalanceHandler implements Handler {
 
     @Override
     public Thread newThread(Runnable r) {
-      return new Thread(r, "retry-pool-thread-" + count.getAndIncrement());
+      Thread thread = new Thread(r, "retry-pool-thread-" + count.getAndIncrement());
+      // avoid block shutdown
+      thread.setDaemon(true);
+      return thread;
     }
   });
 
diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadbalanceHandler.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadbalanceHandler.java
index 5c9ccbd..02061e8 100644
--- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadbalanceHandler.java
+++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadbalanceHandler.java
@@ -22,6 +22,8 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 
 import javax.ws.rs.core.Response.Status;
 import javax.xml.ws.Holder;
@@ -471,4 +473,18 @@ public class TestLoadbalanceHandler {
     Assert.assertTrue(handler.isFailedResponse(Response.create(490, "", "")));
     Assert.assertTrue(handler.isFailedResponse(Response.consumerFailResp(new NullPointerException())));
   }
+
+  @Test
+  public void retryPoolDaemon() throws ExecutionException, InterruptedException {
+    ExecutorService RETRY_POOL = Deencapsulation.getField(handler, "RETRY_POOL");
+
+    Holder<Thread> nameHolder = new Holder<>();
+
+    RETRY_POOL.submit(() -> {
+      nameHolder.value = Thread.currentThread();
+    }).get();
+
+    Assert.assertThat(nameHolder.value.getName(), Matchers.startsWith("retry-pool-thread-"));
+    Assert.assertTrue(nameHolder.value.isDaemon());
+  }
 }

-- 
To stop receiving notification emails like this one, please contact
wujimin@apache.org.

[incubator-servicecomb-java-chassis] 05/06: [SCB-617] avoid MetricsBootstrap shutdown NPE

Posted by wu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit 22faf9d55744c9485996d2852d7db9815b888048
Author: wujimin <wu...@huawei.com>
AuthorDate: Mon May 28 16:30:40 2018 +0800

    [SCB-617] avoid MetricsBootstrap shutdown NPE
---
 .../foundation/metrics/MetricsBootstrap.java         |  6 ++++--
 .../foundation/metrics/MetricsInitializer.java       |  2 +-
 .../foundation/metrics/TestMetricsBootstrap.java     | 20 ++++++++++++++------
 .../metrics/core/DefaultRegistryInitializer.java     |  2 +-
 .../metrics/core/TestDefaultRegistryInitializer.java |  2 +-
 .../metrics/prometheus/PrometheusPublisher.java      |  2 +-
 .../metrics/prometheus/TestPrometheusPublisher.java  |  2 +-
 7 files changed, 23 insertions(+), 13 deletions(-)

diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java
index 1a6c0d1..7f13bf1 100644
--- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java
+++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java
@@ -54,12 +54,14 @@ public class MetricsBootstrap {
   }
 
   public void shutdown() {
-    executorService.shutdown();
+    if (executorService != null) {
+      executorService.shutdown();
+    }
 
     List<MetricsInitializer> initializers = new ArrayList<>(SPIServiceUtils.getSortedService(MetricsInitializer.class));
     Collections.reverse(initializers);
     initializers.forEach(initializer -> {
-      initializer.uninit();
+      initializer.destroy();
     });
   }
 
diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java
index 04f9bd4..b232f48 100644
--- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java
+++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java
@@ -29,7 +29,7 @@ public interface MetricsInitializer {
    */
   void init(CompositeRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config);
 
-  default void uninit() {
+  default void destroy() {
 
   }
 }
diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java
index b73834b..50cb30c 100644
--- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java
+++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java
@@ -102,7 +102,7 @@ public class TestMetricsBootstrap {
 
   @Test
   public void shutdown(@Mocked ScheduledExecutorService scheduledExecutorService) {
-    List<MetricsInitializer> uninitList = new ArrayList<>();
+    List<MetricsInitializer> destroyList = new ArrayList<>();
     MetricsInitializer initializer1 = new MetricsInitializer() {
       @Override
       public int getOrder() {
@@ -114,8 +114,8 @@ public class TestMetricsBootstrap {
       }
 
       @Override
-      public void uninit() {
-        uninitList.add(this);
+      public void destroy() {
+        destroyList.add(this);
       }
     };
 
@@ -130,8 +130,8 @@ public class TestMetricsBootstrap {
       }
 
       @Override
-      public void uninit() {
-        uninitList.add(this);
+      public void destroy() {
+        destroyList.add(this);
       }
     };
 
@@ -145,6 +145,14 @@ public class TestMetricsBootstrap {
 
     bootstrap.shutdown();
 
-    Assert.assertThat(uninitList, Matchers.contains(initializer2, initializer1));
+    Assert.assertThat(destroyList, Matchers.contains(initializer2, initializer1));
+  }
+
+  @Test
+  public void shutdown_notStart() {
+    Assert.assertNull(Deencapsulation.getField(bootstrap, "executorService"));
+
+    // should not throw exception
+    bootstrap.shutdown();
   }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultRegistryInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultRegistryInitializer.java
index 5e81ee1..6bd8146 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultRegistryInitializer.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultRegistryInitializer.java
@@ -49,7 +49,7 @@ public class DefaultRegistryInitializer implements MetricsInitializer {
   }
 
   @Override
-  public void uninit() {
+  public void destroy() {
     DefaultMonitorRegistry.getInstance().unregister(registry);
     globalRegistry.remove(registry);
   }
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultRegistryInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultRegistryInitializer.java
index 8692a80..4b2a1c3 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultRegistryInitializer.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultRegistryInitializer.java
@@ -49,7 +49,7 @@ public class TestDefaultRegistryInitializer {
     Assert.assertEquals(1, registries.size());
     Assert.assertEquals(1, DefaultMonitorRegistry.getInstance().getRegisteredMonitors().size());
 
-    registryInitializer.uninit();
+    registryInitializer.destroy();
 
     Assert.assertEquals(0, registries.size());
     Assert.assertEquals(0, DefaultMonitorRegistry.getInstance().getRegisteredMonitors().size());
diff --git a/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java b/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java
index dd4122c..b1eb9c6 100644
--- a/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java
+++ b/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java
@@ -114,7 +114,7 @@ public class PrometheusPublisher extends Collector implements Collector.Describa
   }
 
   @Override
-  public void uninit() {
+  public void destroy() {
     if (httpServer == null) {
       return;
     }
diff --git a/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java b/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java
index 1bf0766..c953730 100644
--- a/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java
+++ b/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java
@@ -102,6 +102,6 @@ public class TestPrometheusPublisher {
           "count_name{tag1=\"tag1v\",tag2=\"tag2v\",} 1.0\n", IOUtils.toString(is));
     }
 
-    publisher.uninit();
+    publisher.destroy();
   }
 }

-- 
To stop receiving notification emails like this one, please contact
wujimin@apache.org.

[incubator-servicecomb-java-chassis] 02/06: [SCB-617] uninit config center source when shutdown

Posted by wu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit ac505e483163dc1b8cc62057dc4967a8bbbdb541
Author: wujimin <wu...@huawei.com>
AuthorDate: Mon May 28 10:16:37 2018 +0800

    [SCB-617] uninit config center source when shutdown
---
 .../servicecomb/core/CseApplicationListener.java   |  3 ++
 .../org/apache/servicecomb/core/SCBEngine.java     |  6 ++-
 .../org/apache/servicecomb/core/TestSCBEngine.java | 13 ++++-
 .../core/TestCseApplicationListener.java           | 56 ++++++++++++++++++++++
 4 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java b/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java
index 3658687..28b6e73 100644
--- a/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java
+++ b/core/src/main/java/org/apache/servicecomb/core/CseApplicationListener.java
@@ -28,6 +28,7 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.ApplicationEvent;
 import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextClosedEvent;
 import org.springframework.context.event.ContextRefreshedEvent;
 import org.springframework.context.support.AbstractApplicationContext;
 import org.springframework.core.Ordered;
@@ -71,6 +72,8 @@ public class CseApplicationListener
       }
 
       SCBEngine.getInstance().init();
+    } else if (event instanceof ContextClosedEvent) {
+      SCBEngine.getInstance().uninit();
     }
   }
 }
diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java
index d30eb3c..0616c25 100644
--- a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java
+++ b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java
@@ -20,6 +20,7 @@ import java.util.Collection;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.apache.servicecomb.config.ConfigUtil;
 import org.apache.servicecomb.core.BootListener.BootEvent;
 import org.apache.servicecomb.core.BootListener.EventType;
 import org.apache.servicecomb.core.definition.loader.SchemaListenerManager;
@@ -256,7 +257,10 @@ public class SCBEngine {
     VertxUtils.blockCloseVertxByName("config-center");
     VertxUtils.blockCloseVertxByName("transport");
 
-    //Step 6: notify all component do clean works via AFTER_CLOSE Event
+    //Step 6: destroy config center source
+    ConfigUtil.destroyConfigCenterConfigurationSource();
+
+    //Step 7: notify all component do clean works via AFTER_CLOSE Event
     safeTriggerEvent(EventType.AFTER_CLOSE);
   }
 
diff --git a/core/src/test/java/org/apache/servicecomb/core/TestSCBEngine.java b/core/src/test/java/org/apache/servicecomb/core/TestSCBEngine.java
index e452e55..f39636a 100644
--- a/core/src/test/java/org/apache/servicecomb/core/TestSCBEngine.java
+++ b/core/src/test/java/org/apache/servicecomb/core/TestSCBEngine.java
@@ -18,7 +18,9 @@
 package org.apache.servicecomb.core;
 
 import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.apache.servicecomb.config.ConfigUtil;
 import org.apache.servicecomb.core.definition.loader.SchemaListenerManager;
 import org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager;
 import org.apache.servicecomb.core.provider.consumer.ReferenceConfig;
@@ -36,6 +38,8 @@ import org.mockito.Mockito;
 
 import mockit.Expectations;
 import mockit.Injectable;
+import mockit.Mock;
+import mockit.MockUp;
 import mockit.Mocked;
 
 public class TestSCBEngine {
@@ -47,7 +51,6 @@ public class TestSCBEngine {
       @Injectable ConsumerProviderManager consumerProviderManager,
       @Injectable TransportManager transportManager,
       @Injectable AppManager appManager) {
-
     new Expectations(RegistryUtils.class) {
       {
         RegistryUtils.getServiceRegistry().getAppManager();
@@ -56,6 +59,13 @@ public class TestSCBEngine {
         RegistryUtils.destroy();
       }
     };
+    AtomicBoolean configDestroy = new AtomicBoolean();
+    new MockUp<ConfigUtil>() {
+      @Mock
+      void destroyConfigCenterConfigurationSource() {
+        configDestroy.set(true);
+      }
+    };
 
     SchemaListenerManager schemaListenerManager = Mockito.mock(SchemaListenerManager.class);
 
@@ -75,6 +85,7 @@ public class TestSCBEngine {
     engine.uninit();
 
     Assert.assertEquals(SCBStatus.DOWN, engine.getStatus());
+    Assert.assertTrue(configDestroy.get());
   }
 
   @Test
diff --git a/core/src/test/java/org/apache/servicecomb/core/org/apache/servicecomb/core/TestCseApplicationListener.java b/core/src/test/java/org/apache/servicecomb/core/org/apache/servicecomb/core/TestCseApplicationListener.java
new file mode 100644
index 0000000..26d7d78
--- /dev/null
+++ b/core/src/test/java/org/apache/servicecomb/core/org/apache/servicecomb/core/TestCseApplicationListener.java
@@ -0,0 +1,56 @@
+/*
+ * 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.servicecomb.core.org.apache.servicecomb.core;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.servicecomb.core.CseApplicationListener;
+import org.apache.servicecomb.core.SCBEngine;
+import org.apache.servicecomb.core.SCBStatus;
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.context.event.ContextClosedEvent;
+
+import mockit.Mock;
+import mockit.MockUp;
+import mockit.Mocked;
+
+public class TestCseApplicationListener {
+  @Test
+  public void onApplicationEvent_close(@Mocked ContextClosedEvent contextClosedEvent) {
+    AtomicInteger count = new AtomicInteger();
+    SCBEngine scbEngine = new SCBEngine() {
+      @Override
+      public synchronized void uninit() {
+        count.incrementAndGet();
+      }
+    };
+    new MockUp<SCBEngine>() {
+      @Mock
+      SCBEngine getInstance() {
+        return scbEngine;
+      }
+    };
+    scbEngine.setStatus(SCBStatus.UP);
+
+    CseApplicationListener listener = new CseApplicationListener();
+
+    listener.onApplicationEvent(contextClosedEvent);
+
+    Assert.assertEquals(1, count.get());
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
wujimin@apache.org.

[incubator-servicecomb-java-chassis] 01/06: [SCB-617] ConfigCenterConfigurationSource support uninit

Posted by wu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit 2916139bb2d06e5a0d21c586b59707fcf16ce086
Author: wujimin <wu...@huawei.com>
AuthorDate: Mon May 28 08:59:30 2018 +0800

    [SCB-617] ConfigCenterConfigurationSource support uninit
---
 .../ConfigCenterConfigurationSourceImpl.java       | 13 +++++-
 .../config/client/ConfigCenterClient.java          | 32 ++++++++++----
 .../apache/servicecomb/config/client/URIConst.java | 40 +++++------------
 .../TestConfigCenterConfigurationSource.java       | 30 +++++++++++++
 .../config/client/TestConfigCenterClient.java      | 51 ++++++++++++++++++----
 .../servicecomb/config/client/TestURIConst.java    | 26 +++++++++--
 .../org/apache/servicecomb/config/ConfigUtil.java  | 10 +++++
 .../spi/ConfigCenterConfigurationSource.java       |  4 ++
 .../apache/servicecomb/config/TestConfigUtil.java  | 25 +++++++++++
 9 files changed, 181 insertions(+), 50 deletions(-)

diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigCenterConfigurationSourceImpl.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigCenterConfigurationSourceImpl.java
index d439299..f4c12a7 100644
--- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigCenterConfigurationSourceImpl.java
+++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigCenterConfigurationSourceImpl.java
@@ -49,6 +49,8 @@ public class ConfigCenterConfigurationSourceImpl implements ConfigCenterConfigur
 
   private UpdateHandler updateHandler = new UpdateHandler();
 
+  private ConfigCenterClient configCenterClient;
+
   private static final String CONFIG_CENTER_URL_KEY = "cse.config.client.serverUri";
 
   public ConfigCenterConfigurationSourceImpl() {
@@ -64,7 +66,7 @@ public class ConfigCenterConfigurationSourceImpl implements ConfigCenterConfigur
   }
 
   private void init() {
-    ConfigCenterClient configCenterClient = new ConfigCenterClient(updateHandler);
+    configCenterClient = new ConfigCenterClient(updateHandler);
     configCenterClient.connectServer();
   }
 
@@ -74,6 +76,15 @@ public class ConfigCenterConfigurationSourceImpl implements ConfigCenterConfigur
     init();
   }
 
+  @Override
+  public void destroy() {
+    if (configCenterClient == null) {
+      return;
+    }
+
+    configCenterClient.destroy();
+  }
+
   public void addUpdateListener(WatchedUpdateListener watchedUpdateListener) {
     if (watchedUpdateListener != null) {
       listeners.add(watchedUpdateListener);
diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/ConfigCenterClient.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/ConfigCenterClient.java
index 9b78429..9941a27 100644
--- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/ConfigCenterClient.java
+++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/ConfigCenterClient.java
@@ -83,12 +83,12 @@ public class ConfigCenterClient {
 
   private static final String SSL_KEY = "cc.consumer";
 
-  private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1);
-
   private static final long HEARTBEAT_INTERVAL = 30000;
 
   private static final long BOOTUP_WAIT_TIME = 10;
 
+  private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
+
   private ScheduledExecutorService heartbeatTask = null;
 
   private int refreshMode = CONFIG_CENTER_CONFIG.getRefreshMode();
@@ -109,13 +109,15 @@ public class ConfigCenterClient {
 
   private ConfigCenterConfigurationSourceImpl.UpdateHandler updateHandler;
 
-  private static ClientPoolManager<HttpClientWithContext> clientMgr;
+  private ClientPoolManager<HttpClientWithContext> clientMgr;
 
   private boolean isWatching = false;
 
-  private static final ServiceLoader<AuthHeaderProvider> authHeaderProviders =
+  private final ServiceLoader<AuthHeaderProvider> authHeaderProviders =
       ServiceLoader.load(AuthHeaderProvider.class);
 
+  private URIConst uriConst = new URIConst();
+
   public ConfigCenterClient(ConfigCenterConfigurationSourceImpl.UpdateHandler updateHandler) {
     this.updateHandler = updateHandler;
   }
@@ -134,18 +136,30 @@ public class ConfigCenterClient {
     refreshMembers(memberDiscovery);
     ConfigRefresh refreshTask = new ConfigRefresh(parseConfigUtils, memberDiscovery);
     refreshTask.run(true);
-    EXECUTOR.scheduleWithFixedDelay(refreshTask,
+    executor.scheduleWithFixedDelay(refreshTask,
         firstRefreshInterval,
         refreshInterval,
         TimeUnit.MILLISECONDS);
   }
 
+  public void destroy() {
+    if (executor != null) {
+      executor.shutdown();
+      executor = null;
+    }
+
+    if (heartbeatTask != null) {
+      heartbeatTask.shutdown();
+      heartbeatTask = null;
+    }
+  }
+
   private void refreshMembers(MemberDiscovery memberDiscovery) {
     if (CONFIG_CENTER_CONFIG.getAutoDiscoveryEnabled()) {
       String configCenter = memberDiscovery.getConfigServer();
       IpPort ipPort = NetUtils.parseIpPortFromURI(configCenter);
       clientMgr.findThreadBindClientPool().runOnContext(client -> {
-        HttpClientRequest request = client.get(ipPort.getPort(), ipPort.getHostOrIp(), URIConst.MEMBERS, rsp -> {
+        HttpClientRequest request = client.get(ipPort.getPort(), ipPort.getHostOrIp(), uriConst.MEMBERS, rsp -> {
           if (rsp.statusCode() == HttpResponseStatus.OK.code()) {
             rsp.bodyHandler(buf -> {
               memberDiscovery.refreshMembers(buf.toJsonObject());
@@ -153,7 +167,7 @@ public class ConfigCenterClient {
           }
         });
         SignRequest signReq = createSignRequest(request.method().toString(),
-            configCenter + URIConst.MEMBERS,
+            configCenter + uriConst.MEMBERS,
             new HashMap<>(),
             null);
         if (ConfigCenterConfig.INSTANCE.getToken() != null) {
@@ -247,7 +261,7 @@ public class ConfigCenterClient {
         throws UnsupportedEncodingException, InterruptedException {
       CountDownLatch waiter = new CountDownLatch(1);
       IpPort ipPort = NetUtils.parseIpPortFromURI(configCenter);
-      String url = URIConst.REFRESH_ITEMS + "?dimensionsInfo="
+      String url = uriConst.REFRESH_ITEMS + "?dimensionsInfo="
           + StringUtils.deleteWhitespace(URLEncoder.encode(serviceName, "UTF-8"));
       Map<String, String> headers = new HashMap<>();
       headers.put("x-domain-name", tenantName);
@@ -337,7 +351,7 @@ public class ConfigCenterClient {
         LOGGER.error("encode failed. Error message: {}", e.getMessage());
         encodeServiceName = StringUtils.deleteWhitespace(serviceName);
       }
-      String path = URIConst.ITEMS + "?dimensionsInfo=" + encodeServiceName;
+      String path = uriConst.ITEMS + "?dimensionsInfo=" + encodeServiceName;
       clientMgr.findThreadBindClientPool().runOnContext(client -> {
         IpPort ipPort = NetUtils.parseIpPortFromURI(configcenter);
         HttpClientRequest request = client.get(ipPort.getPort(), ipPort.getHostOrIp(), path, rsp -> {
diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/URIConst.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/URIConst.java
index 331c744..14be671 100644
--- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/URIConst.java
+++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/URIConst.java
@@ -18,41 +18,25 @@
 package org.apache.servicecomb.config.client;
 
 public class URIConst {
-  public static final String DOMAIN_NAME = ConfigCenterConfig.INSTANCE.getDomainName();
+  private final String DOMAIN_NAME = ConfigCenterConfig.INSTANCE.getDomainName();
 
-  public static final String CURRENT_VERSION = ConfigCenterConfig.INSTANCE.getApiVersion();
+  private final String CURRENT_VERSION = ConfigCenterConfig.INSTANCE.getApiVersion();
 
-  public static final String VERSION_V2 = "v2";
+  private final String VERSION_V2 = "v2";
 
-  public static final String PREFIX_V3 = String.format("/v3/%s/configuration", DOMAIN_NAME);
+  private final String PREFIX_V2 = "/configuration/v2";
 
-  public static final String MEMBERS;
+  private final String PREFIX_V3 = String.format("/v3/%s/configuration", DOMAIN_NAME);
 
-  static {
-    if (VERSION_V2.equals(CURRENT_VERSION)) {
-      MEMBERS = "/members";
-    } else {
-      MEMBERS = PREFIX_V3 + "/members";
-    }
-  }
+  private final boolean isV2 = VERSION_V2.equals(CURRENT_VERSION);
 
-  public static final String REFRESH_ITEMS;
+  private final String CURRENT_PREFIX = isV2 ? PREFIX_V2 : PREFIX_V3;
 
-  static {
-    if (VERSION_V2.equals(CURRENT_VERSION)) {
-      REFRESH_ITEMS = "/configuration/v2/refresh/items";
-    } else {
-      REFRESH_ITEMS = PREFIX_V3 + "/refresh/items";
-    }
-  }
+  // v2 no prefix
+  public final String MEMBERS = isV2 ? "/members" : PREFIX_V3 + "/members";
 
-  public static final String ITEMS;
+  // v2 has prefix
+  public final String REFRESH_ITEMS = CURRENT_PREFIX + "/refresh/items";
 
-  static {
-    if (VERSION_V2.equals(CURRENT_VERSION)) {
-      ITEMS = "/configuration/v2/items";
-    } else {
-      ITEMS = PREFIX_V3 + "/items";
-    }
-  }
+  public final String ITEMS = CURRENT_PREFIX + "/items";
 }
diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/archaius/sources/TestConfigCenterConfigurationSource.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/archaius/sources/TestConfigCenterConfigurationSource.java
index 728c262..ec45127 100644
--- a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/archaius/sources/TestConfigCenterConfigurationSource.java
+++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/archaius/sources/TestConfigCenterConfigurationSource.java
@@ -18,8 +18,11 @@ package org.apache.servicecomb.config.archaius.sources;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.commons.lang.reflect.FieldUtils;
 import org.apache.servicecomb.config.archaius.sources.ConfigCenterConfigurationSourceImpl.UpdateHandler;
+import org.apache.servicecomb.config.client.ConfigCenterClient;
 import org.junit.Assert;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -28,6 +31,8 @@ import com.netflix.config.WatchedUpdateListener;
 import com.netflix.config.WatchedUpdateResult;
 
 import mockit.Deencapsulation;
+import mockit.Mock;
+import mockit.MockUp;
 
 /**
  * Created by on 2017/1/12.
@@ -91,4 +96,29 @@ public class TestConfigCenterConfigurationSource {
     configCenterSource.removeUpdateListener(watchedUpdateListener);
     Assert.assertTrue(configCenterSource.getCurrentListeners().isEmpty());
   }
+
+  @Test
+  public void destroy_notInit() {
+    ConfigCenterConfigurationSourceImpl configCenterSource = new ConfigCenterConfigurationSourceImpl();
+
+    // need not throw exception
+    configCenterSource.destroy();
+  }
+
+  @Test
+  public void destroy_inited() throws IllegalAccessException {
+    AtomicInteger count = new AtomicInteger();
+    ConfigCenterClient configCenterClient = new MockUp<ConfigCenterClient>() {
+      @Mock
+      void destroy() {
+        count.incrementAndGet();
+      }
+    }.getMockInstance();
+    ConfigCenterConfigurationSourceImpl configCenterSource = new ConfigCenterConfigurationSourceImpl();
+    FieldUtils.writeDeclaredField(configCenterSource, "configCenterClient", configCenterClient, true);
+
+    configCenterSource.destroy();
+
+    Assert.assertEquals(1, count.get());
+  }
 }
diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/client/TestConfigCenterClient.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/client/TestConfigCenterClient.java
index 0f9013c..83398f1 100644
--- a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/client/TestConfigCenterClient.java
+++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/client/TestConfigCenterClient.java
@@ -20,12 +20,15 @@ package org.apache.servicecomb.config.client;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 
 import org.apache.servicecomb.config.ConfigUtil;
 import org.apache.servicecomb.config.archaius.sources.ConfigCenterConfigurationSourceImpl;
 import org.apache.servicecomb.config.archaius.sources.ConfigCenterConfigurationSourceImpl.UpdateHandler;
 import org.apache.servicecomb.config.client.ConfigCenterClient.ConfigRefresh;
 import org.apache.servicecomb.foundation.common.event.EventManager;
+import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager;
 import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext;
 import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext.RunHandler;
 import org.junit.Assert;
@@ -45,8 +48,10 @@ import io.vertx.core.http.HttpMethod;
 import io.vertx.core.http.WebSocket;
 import io.vertx.core.json.JsonObject;
 import mockit.Deencapsulation;
+import mockit.Expectations;
 import mockit.Mock;
 import mockit.MockUp;
+import mockit.Mocked;
 
 public class TestConfigCenterClient {
   @BeforeClass
@@ -110,9 +115,9 @@ public class TestConfigCenterClient {
 
   @SuppressWarnings("unchecked")
   @Test
-  public void testConfigRefresh() {
+  public void testConfigRefresh(@Mocked ClientPoolManager<HttpClientWithContext> clientMgr,
+      @Mocked HttpClientWithContext httpClientWithContext) {
     ConfigCenterConfigurationSourceImpl impl = new ConfigCenterConfigurationSourceImpl();
-    impl.init(ConfigUtil.createLocalConfig());
     UpdateHandler updateHandler = impl.new UpdateHandler();
     HttpClientRequest request = Mockito.mock(HttpClientRequest.class);
     Mockito.when(request.headers()).thenReturn(MultiMap.caseInsensitiveMultiMap());
@@ -120,13 +125,13 @@ public class TestConfigCenterClient {
     Mockito.when(rsp.toString())
         .thenReturn("{\"application\":{\"2\":\"2\",\"aa\":\"1\"},\"vmalledge\":{\"aa\":\"3\"}}");
 
-    HttpClientResponse event = Mockito.mock(HttpClientResponse.class);
-    Mockito.when(event.bodyHandler(Mockito.any(Handler.class))).then(invocation -> {
+    HttpClientResponse httpClientResponse = Mockito.mock(HttpClientResponse.class);
+    Mockito.when(httpClientResponse.bodyHandler(Mockito.any(Handler.class))).then(invocation -> {
       Handler<Buffer> handler = invocation.getArgumentAt(0, Handler.class);
       handler.handle(rsp);
       return null;
     });
-    Mockito.when(event.statusCode()).thenReturn(200);
+    Mockito.when(httpClientResponse.statusCode()).thenReturn(200);
 
     Buffer buf = Mockito.mock(Buffer.class);
     Mockito.when(buf.toJsonObject()).thenReturn(new JsonObject(
@@ -142,7 +147,7 @@ public class TestConfigCenterClient {
         httpClient.get(Mockito.anyInt(), Mockito.anyString(), Mockito.anyString(), Mockito.any(Handler.class)))
         .then(invocation -> {
           Handler<HttpClientResponse> handler = invocation.getArgumentAt(3, Handler.class);
-          handler.handle(event);
+          handler.handle(httpClientResponse);
           return request;
         });
     Mockito.when(httpClient.websocket(Mockito.anyInt(),
@@ -162,7 +167,15 @@ public class TestConfigCenterClient {
         handler.run(httpClient);
       }
     };
+    new Expectations() {
+      {
+        clientMgr.findThreadBindClientPool();
+        result = httpClientWithContext;
+      }
+    };
+
     ConfigCenterClient cc = new ConfigCenterClient(updateHandler);
+    Deencapsulation.setField(cc, "clientMgr", clientMgr);
     ParseConfigUtils parseConfigUtils = new ParseConfigUtils(updateHandler);
     MemberDiscovery memberdis = new MemberDiscovery(Arrays.asList("http://configcentertest:30103"));
     ConfigRefresh refresh = cc.new ConfigRefresh(parseConfigUtils, memberdis);
@@ -175,7 +188,8 @@ public class TestConfigCenterClient {
 
   @SuppressWarnings("unchecked")
   @Test
-  public void testConfigRefreshException() {
+  public void testConfigRefreshException(@Mocked ClientPoolManager<HttpClientWithContext> clientMgr,
+      @Mocked HttpClientWithContext httpClientWithContext) {
     ConfigCenterConfigurationSourceImpl impl = new ConfigCenterConfigurationSourceImpl();
     Map<String, String> map = new HashMap<>();
     EventManager.register(new Object() {
@@ -189,7 +203,6 @@ public class TestConfigCenterClient {
         }
       }
     });
-    impl.init(ConfigUtil.createLocalConfig());
     UpdateHandler updateHandler = impl.new UpdateHandler();
     HttpClientRequest request = Mockito.mock(HttpClientRequest.class);
     Mockito.when(request.headers()).thenReturn(MultiMap.caseInsensitiveMultiMap());
@@ -221,7 +234,14 @@ public class TestConfigCenterClient {
         handler.run(httpClient);
       }
     };
+    new Expectations() {
+      {
+        clientMgr.findThreadBindClientPool();
+        result = httpClientWithContext;
+      }
+    };
     ConfigCenterClient cc = new ConfigCenterClient(updateHandler);
+    Deencapsulation.setField(cc, "clientMgr", clientMgr);
     ParseConfigUtils parseConfigUtils = new ParseConfigUtils(updateHandler);
     MemberDiscovery memberdis = new MemberDiscovery(Arrays.asList("http://configcentertest:30103"));
     ConfigRefresh refresh = cc.new ConfigRefresh(parseConfigUtils, memberdis);
@@ -231,4 +251,19 @@ public class TestConfigCenterClient {
     refresh.run();
     Assert.assertEquals("Succ event trigger", map.get("result"));
   }
+
+  @Test
+  public void destroy() {
+    ConfigCenterClient configCenterClient = new ConfigCenterClient(null);
+    ScheduledExecutorService executor = Deencapsulation.getField(configCenterClient, "executor");
+    ScheduledExecutorService heartbeatTask = Executors.newScheduledThreadPool(1);
+    Deencapsulation.setField(configCenterClient, "heartbeatTask", heartbeatTask);
+
+    Assert.assertFalse(executor.isShutdown());
+
+    configCenterClient.destroy();
+
+    Assert.assertTrue(executor.isShutdown());
+    Assert.assertTrue(heartbeatTask.isShutdown());
+  }
 }
diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/client/TestURIConst.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/client/TestURIConst.java
index 51eeffb..8877624 100644
--- a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/client/TestURIConst.java
+++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/client/TestURIConst.java
@@ -25,7 +25,24 @@ import mockit.Mocked;
 
 public class TestURIConst {
   @Test
-  public void testURI(final @Mocked ConfigCenterConfig config) {
+  public void testURI_v2(final @Mocked ConfigCenterConfig config) {
+    new Expectations() {
+      {
+        config.getDomainName();
+        result = "mytenant";
+        config.getApiVersion();
+        result = "v2";
+      }
+    };
+
+    URIConst uriConst = new URIConst();
+    Assert.assertEquals("/members", uriConst.MEMBERS);
+    Assert.assertEquals("/configuration/v2/refresh/items", uriConst.REFRESH_ITEMS);
+    Assert.assertEquals("/configuration/v2/items", uriConst.ITEMS);
+  }
+
+  @Test
+  public void testURI_v3(final @Mocked ConfigCenterConfig config) {
     new Expectations() {
       {
         config.getDomainName();
@@ -35,8 +52,9 @@ public class TestURIConst {
       }
     };
 
-    Assert.assertEquals(URIConst.MEMBERS, "/v3/mytenant/configuration/members");
-    Assert.assertEquals(URIConst.REFRESH_ITEMS, "/v3/mytenant/configuration/refresh/items");
-    Assert.assertEquals(URIConst.ITEMS, "/v3/mytenant/configuration/items");
+    URIConst uriConst = new URIConst();
+    Assert.assertEquals("/v3/mytenant/configuration/members", uriConst.MEMBERS);
+    Assert.assertEquals("/v3/mytenant/configuration/refresh/items", uriConst.REFRESH_ITEMS);
+    Assert.assertEquals("/v3/mytenant/configuration/items", uriConst.ITEMS);
   }
 }
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java
index 6e2a6f6..27f2161 100644
--- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java
@@ -241,6 +241,16 @@ public final class ConfigUtil {
     }
   }
 
+  public static void destroyConfigCenterConfigurationSource() {
+    SPIServiceUtils.getAllService(ConfigCenterConfigurationSource.class).forEach(source -> {
+      try {
+        source.destroy();
+      } catch (Throwable e) {
+        LOGGER.error("Failed to destroy {}", source.getClass().getName());
+      }
+    });
+  }
+
   public static void addExtraConfig(String extraConfigName, Map<String, Object> extraConfig) {
     EXTRA_CONFIG_MAP.put(extraConfigName, extraConfig);
   }
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSource.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSource.java
index 5769c55..cc651d9 100644
--- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSource.java
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSource.java
@@ -25,4 +25,8 @@ public interface ConfigCenterConfigurationSource extends WatchedConfigurationSou
   boolean isValidSource(Configuration localConfiguration);
 
   void init(Configuration localConfiguration);
+
+  default void destroy() {
+
+  }
 }
diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java
index 119c6ad..c56bb7d 100644
--- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java
+++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java
@@ -28,6 +28,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.commons.configuration.AbstractConfiguration;
 import org.apache.servicecomb.config.archaius.sources.ConfigModel;
@@ -48,6 +49,8 @@ import com.netflix.config.DynamicWatchedConfiguration;
 
 import mockit.Deencapsulation;
 import mockit.Expectations;
+import mockit.Mock;
+import mockit.MockUp;
 
 public class TestConfigUtil {
 
@@ -343,4 +346,26 @@ public class TestConfigUtil {
       }
     }
   }
+
+  @Test
+  public void destroyConfigCenterConfigurationSource() {
+    AtomicInteger count = new AtomicInteger();
+    ConfigCenterConfigurationSource source = new MockUp<ConfigCenterConfigurationSource>() {
+      @Mock
+      void destroy() {
+        count.incrementAndGet();
+      }
+    }.getMockInstance();
+
+    new Expectations(SPIServiceUtils.class) {
+      {
+        SPIServiceUtils.getAllService(ConfigCenterConfigurationSource.class);
+        result = Arrays.asList(source, source);
+      }
+    };
+
+    ConfigUtil.destroyConfigCenterConfigurationSource();
+
+    Assert.assertEquals(2, count.get());
+  }
 }

-- 
To stop receiving notification emails like this one, please contact
wujimin@apache.org.