Merge branch 'ralf0131-graceful-shutdown-in-tomcat'
diff --git a/all/pom.xml b/all/pom.xml
index 09bfe85..edc0c4a 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -321,6 +321,13 @@
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
+ <artifactId>dubbo-bootstrap</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
<artifactId>hessian-lite</artifactId>
<version>3.2.2</version>
<scope>compile</scope>
@@ -418,6 +425,7 @@
<include>com.alibaba:dubbo-serialization-fst</include>
<include>com.alibaba:dubbo-serialization-kryo</include>
<include>com.alibaba:dubbo-serialization-jdk</include>
+ <include>com.alibaba:dubbo-bootstrap</include>
</includes>
</artifactSet>
<transformers>
diff --git a/dubbo-bootstrap/pom.xml b/dubbo-bootstrap/pom.xml
new file mode 100644
index 0000000..bb2b1a4
--- /dev/null
+++ b/dubbo-bootstrap/pom.xml
@@ -0,0 +1,47 @@
+<!--
+ 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>dubbo-parent</artifactId>
+ <groupId>com.alibaba</groupId>
+ <version>2.6.2-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>dubbo-bootstrap</artifactId>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-config-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java b/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java
new file mode 100644
index 0000000..8694e48
--- /dev/null
+++ b/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.bootstrap;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.ServiceConfig;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+import com.alibaba.dubbo.rpc.Protocol;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A bootstrap class to easily start and stop Dubbo via programmatic API.
+ * The bootstrap class will be responsible to cleanup the resources during stop.
+ */
+public class DubboBootstrap {
+
+ private static final Logger logger = LoggerFactory.getLogger(DubboBootstrap.class);
+
+ /**
+ * The list of ServiceConfig
+ */
+ private List<ServiceConfig> serviceConfigList;
+
+ /**
+ * Has it already been destroyed or not?
+ */
+ private final AtomicBoolean destroyed;
+
+ /**
+ * The shutdown hook used when Dubbo is running under embedded environment
+ */
+ private Thread shutdownHook;
+
+ public DubboBootstrap() {
+ this.serviceConfigList = new ArrayList<ServiceConfig>();
+ this.destroyed = new AtomicBoolean(false);
+ this.shutdownHook = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Run shutdown hook now.");
+ }
+ destroy();
+ }
+ }, "DubboShutdownHook");
+ }
+
+ /**
+ * Register service config to bootstrap, which will be called during {@link DubboBootstrap#stop()}
+ * @param serviceConfig the service
+ * @return the bootstrap instance
+ */
+ public DubboBootstrap regsiterServiceConfig(ServiceConfig serviceConfig) {
+ serviceConfigList.add(serviceConfig);
+ return this;
+ }
+
+ public void start() {
+ registerShutdownHook();
+ for (ServiceConfig serviceConfig: serviceConfigList) {
+ serviceConfig.export();
+ }
+ }
+
+ public void stop() {
+ for (ServiceConfig serviceConfig: serviceConfigList) {
+ serviceConfig.unexport();
+ }
+ destroy();
+ removeShutdownHook();
+ }
+
+ /**
+ * Register the shutdown hook
+ */
+ public void registerShutdownHook() {
+ Runtime.getRuntime().addShutdownHook(shutdownHook);
+ }
+
+ /**
+ * Remove this shutdown hook
+ */
+ public void removeShutdownHook() {
+ try {
+ Runtime.getRuntime().removeShutdownHook(shutdownHook);
+ }
+ catch (IllegalStateException ex) {
+ // ignore - VM is already shutting down
+ }
+ }
+
+ /**
+ * Destroy all the resources, including registries and protocols.
+ */
+ private void destroy() {
+ if (!destroyed.compareAndSet(false, true)) {
+ return;
+ }
+ // destroy all the registries
+ AbstractRegistryFactory.destroyAll();
+ // destroy all the protocols
+ ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
+ for (String protocolName : loader.getLoadedExtensions()) {
+ try {
+ Protocol protocol = loader.getLoadedExtension(protocolName);
+ if (protocol != null) {
+ protocol.destroy();
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java
index 6b07cc8..d605e13 100644
--- a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java
@@ -43,19 +43,27 @@
return false;
}
+ /**
+ * Use the shutdown pattern from:
+ * https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
+ * @param executor the Executor to shutdown
+ * @param timeout the timeout in milliseconds before termination
+ */
public static void gracefulShutdown(Executor executor, int timeout) {
if (!(executor instanceof ExecutorService) || isTerminated(executor)) {
return;
}
final ExecutorService es = (ExecutorService) executor;
try {
- es.shutdown(); // Disable new tasks from being submitted
+ // Disable new tasks from being submitted
+ es.shutdown();
} catch (SecurityException ex2) {
return;
} catch (NullPointerException ex2) {
return;
}
try {
+ // Wait a while for existing tasks to terminate
if (!es.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {
es.shutdownNow();
}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
index 73fc07d..8d8d182 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
@@ -73,18 +73,6 @@
legacyProperties.put("dubbo.service.url", "dubbo.service.address");
}
- static {
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- @Override
- public void run() {
- if (logger.isInfoEnabled()) {
- logger.info("Run shutdown hook now.");
- }
- ProtocolConfig.destroyAll();
- }
- }, "DubboShutdownHook"));
- }
-
protected String id;
private static String convertLegacyValue(String key, String value) {
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
index 0e7a524..b15129b 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
@@ -21,7 +21,6 @@
import com.alibaba.dubbo.common.status.StatusChecker;
import com.alibaba.dubbo.common.threadpool.ThreadPool;
import com.alibaba.dubbo.config.support.Parameter;
-import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
import com.alibaba.dubbo.remoting.Codec;
import com.alibaba.dubbo.remoting.Dispatcher;
import com.alibaba.dubbo.remoting.Transporter;
@@ -30,7 +29,6 @@
import com.alibaba.dubbo.rpc.Protocol;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* ProtocolConfig
@@ -135,8 +133,6 @@
// if it's default
private Boolean isDefault;
- private static final AtomicBoolean destroyed = new AtomicBoolean(false);
-
public ProtocolConfig() {
}
@@ -149,27 +145,6 @@
setPort(port);
}
- // TODO: 2017/8/30 to move this method somewhere else
- public static void destroyAll() {
- if (!destroyed.compareAndSet(false, true)) {
- return;
- }
-
- AbstractRegistryFactory.destroyAll();
-
- ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
- for (String protocolName : loader.getLoadedExtensions()) {
- try {
- Protocol protocol = loader.getLoadedExtension(protocolName);
- if (protocol != null) {
- protocol.destroy();
- }
- } catch (Throwable t) {
- logger.warn(t.getMessage(), t);
- }
- }
- }
-
@Parameter(excluded = true)
public String getName() {
return name;
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
index 7e2672b..e5684f3 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
@@ -18,7 +18,6 @@
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.config.support.Parameter;
-import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
import java.util.Map;
@@ -96,13 +95,9 @@
setAddress(address);
}
- public static void destroyAll() {
- AbstractRegistryFactory.destroyAll();
- }
-
- @Deprecated
- public static void closeAll() {
- destroyAll();
+ public RegistryConfig(String address, String protocol) {
+ setAddress(address);
+ setProtocol(protocol);
}
public String getProtocol() {
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java
index c9d9046..4d10c72 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java
@@ -17,7 +17,6 @@
package com.alibaba.dubbo.config;
-import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.config.mock.MockProtocol2;
import com.alibaba.dubbo.rpc.Protocol;
import org.junit.Test;
@@ -33,15 +32,6 @@
import static org.junit.Assert.assertThat;
public class ProtocolConfigTest {
- @Test
- public void testDestroyAll() throws Exception {
- Protocol protocol = Mockito.mock(Protocol.class);
- MockProtocol2.delegate = protocol;
- ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
- loader.getExtension("mockprotocol2");
- ProtocolConfig.destroyAll();
- Mockito.verify(protocol).destroy();
- }
@Test
public void testDestroy() throws Exception {
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java.orig b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java.orig
new file mode 100644
index 0000000..c22910b
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java.orig
@@ -0,0 +1,214 @@
+/*
+ * 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 com.alibaba.dubbo.config;
+
+<<<<<<< HEAD
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.config.mock.MockProtocol2;
+=======
+>>>>>>> e201004e985f3ae43ee8c65baa16bcc0aecc0000
+import com.alibaba.dubbo.rpc.Protocol;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class ProtocolConfigTest {
+
+ @Test
+ public void testDestroy() throws Exception {
+ Protocol protocol = Mockito.mock(Protocol.class);
+ MockProtocol2.delegate = protocol;
+ ProtocolConfig protocolConfig = new ProtocolConfig();
+ protocolConfig.setName("mockprotocol2");
+ protocolConfig.destory();
+ Mockito.verify(protocol).destroy();
+ }
+
+ @Test
+ public void testName() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setName("name");
+ Map<String, String> parameters = new HashMap<String, String>();
+ ProtocolConfig.appendParameters(parameters, protocol);
+ assertThat(protocol.getName(), equalTo("name"));
+ assertThat(protocol.getId(), equalTo("name"));
+ assertThat(parameters.isEmpty(), is(true));
+ }
+
+ @Test
+ public void testHost() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setHost("host");
+ Map<String, String> parameters = new HashMap<String, String>();
+ ProtocolConfig.appendParameters(parameters, protocol);
+ assertThat(protocol.getHost(), equalTo("host"));
+ assertThat(parameters.isEmpty(), is(true));
+ }
+
+ @Test
+ public void testPort() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setPort(8080);
+ Map<String, String> parameters = new HashMap<String, String>();
+ ProtocolConfig.appendParameters(parameters, protocol);
+ assertThat(protocol.getPort(), equalTo(8080));
+ assertThat(parameters.isEmpty(), is(true));
+ }
+
+ @Test
+ public void testPath() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setContextpath("context-path");
+ Map<String, String> parameters = new HashMap<String, String>();
+ ProtocolConfig.appendParameters(parameters, protocol);
+ assertThat(protocol.getPath(), equalTo("context-path"));
+ assertThat(protocol.getContextpath(), equalTo("context-path"));
+ assertThat(parameters.isEmpty(), is(true));
+ protocol.setPath("path");
+ assertThat(protocol.getPath(), equalTo("path"));
+ assertThat(protocol.getContextpath(), equalTo("path"));
+ }
+
+ @Test
+ public void testThreads() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setThreads(10);
+ assertThat(protocol.getThreads(), is(10));
+ }
+
+ @Test
+ public void testIothreads() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setIothreads(10);
+ assertThat(protocol.getIothreads(), is(10));
+ }
+
+ @Test
+ public void testQueues() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setQueues(10);
+ assertThat(protocol.getQueues(), is(10));
+ }
+
+ @Test
+ public void testAccepts() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setAccepts(10);
+ assertThat(protocol.getAccepts(), is(10));
+ }
+
+ @Test
+ public void testCodec() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setName("dubbo");
+ protocol.setCodec("mockcodec");
+ assertThat(protocol.getCodec(), equalTo("mockcodec"));
+ }
+
+ @Test
+ public void testAccesslog() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setAccesslog("access.log");
+ assertThat(protocol.getAccesslog(), equalTo("access.log"));
+ }
+
+ @Test
+ public void testTelnet() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setTelnet("mocktelnethandler");
+ assertThat(protocol.getTelnet(), equalTo("mocktelnethandler"));
+ }
+
+ @Test
+ public void testRegister() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setRegister(true);
+ assertThat(protocol.isRegister(), is(true));
+ }
+
+ @Test
+ public void testTransporter() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setTransporter("mocktransporter");
+ assertThat(protocol.getTransporter(), equalTo("mocktransporter"));
+ }
+
+ @Test
+ public void testExchanger() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setExchanger("mockexchanger");
+ assertThat(protocol.getExchanger(), equalTo("mockexchanger"));
+ }
+
+ @Test
+ public void testDispatcher() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setDispatcher("mockdispatcher");
+ assertThat(protocol.getDispatcher(), equalTo("mockdispatcher"));
+ }
+
+ @Test
+ public void testNetworker() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setNetworker("networker");
+ assertThat(protocol.getNetworker(), equalTo("networker"));
+ }
+
+ @Test
+ public void testParameters() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setParameters(Collections.singletonMap("k1", "v1"));
+ assertThat(protocol.getParameters(), hasEntry("k1", "v1"));
+ }
+
+ @Test
+ public void testDefault() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setDefault(true);
+ assertThat(protocol.isDefault(), is(true));
+ }
+
+ @Test
+ public void testKeepAlive() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setKeepAlive(true);
+ assertThat(protocol.getKeepAlive(), is(true));
+ }
+
+ @Test
+ public void testOptimizer() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setOptimizer("optimizer");
+ assertThat(protocol.getOptimizer(), equalTo("optimizer"));
+ }
+
+ @Test
+ public void testExtension() throws Exception {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setExtension("extension");
+ assertThat(protocol.getExtension(), equalTo("extension"));
+ }
+}
diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml
index c7a6660..170ac2c 100644
--- a/dubbo-config/dubbo-config-spring/pom.xml
+++ b/dubbo-config/dubbo-config-spring/pom.xml
@@ -36,14 +36,28 @@
<version>${project.parent.version}</version>
</dependency>
<dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-bootstrap</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-registry-default</artifactId>
<version>${project.parent.version}</version>
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListener.java
new file mode 100644
index 0000000..43ee49d
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListener.java
@@ -0,0 +1,45 @@
+/*
+ * 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 com.alibaba.dubbo.config.spring.initializer;
+
+import org.apache.dubbo.bootstrap.DubboBootstrap;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+/**
+ * An application listener that listens the ContextClosedEvent.
+ * Upon the event, this listener will do the necessary clean up to avoid memory leak.
+ */
+public class DubboApplicationListener implements ApplicationListener<ApplicationEvent> {
+
+ private DubboBootstrap dubboBootstrap;
+
+ public DubboApplicationListener() {
+ dubboBootstrap = new DubboBootstrap();
+ }
+
+ @Override
+ public void onApplicationEvent(ApplicationEvent applicationEvent) {
+ if (applicationEvent instanceof ContextRefreshedEvent) {
+ dubboBootstrap.start();
+ } else if (applicationEvent instanceof ContextClosedEvent) {
+ dubboBootstrap.stop();
+ }
+ }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboWebApplicationInitializer.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboWebApplicationInitializer.java
new file mode 100644
index 0000000..8d0f79d
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboWebApplicationInitializer.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.alibaba.dubbo.config.spring.initializer;
+
+import org.springframework.web.context.AbstractContextLoaderInitializer;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.XmlWebApplicationContext;
+
+/**
+ * An initializer to register {@link DubboApplicationListener}
+ * to the ApplicationContext seamlessly.
+ */
+public class DubboWebApplicationInitializer extends AbstractContextLoaderInitializer {
+
+ /**
+ * This method won't be triggered if running on spring-boot.
+ * It only works when running under a servlet container.
+ * @return a WebApplicationContext with DubboApplicationListener registered.
+ */
+ @Override
+ protected WebApplicationContext createRootApplicationContext() {
+ XmlWebApplicationContext webApplicationContext = new XmlWebApplicationContext();
+ webApplicationContext.addApplicationListener(new DubboApplicationListener());
+ return webApplicationContext;
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
index ced0efa..e081957 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
@@ -19,6 +19,7 @@
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
import com.alibaba.dubbo.common.utils.NamedThreadFactory;
import com.alibaba.dubbo.registry.NotifyListener;
@@ -57,9 +58,14 @@
private final ConcurrentMap<URL, Map<NotifyListener, List<URL>>> failedNotified = new ConcurrentHashMap<URL, Map<NotifyListener, List<URL>>>();
+ /**
+ * The time in milliseconds the retryExecutor will wait
+ */
+ private final int retryPeriod;
+
public FailbackRegistry(URL url) {
super(url);
- int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
+ this.retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
@@ -440,6 +446,7 @@
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
+ ExecutorUtil.gracefulShutdown(retryExecutor, retryPeriod);
}
// ==== Template method ====
diff --git a/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java b/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
index 9d5ab70..bf8056e 100644
--- a/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
+++ b/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
@@ -21,6 +21,7 @@
import com.alibaba.dubbo.common.Version;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
import com.alibaba.dubbo.common.utils.NamedThreadFactory;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.registry.NotifyListener;
@@ -47,7 +48,7 @@
private static final int RECONNECT_PERIOD_DEFAULT = 3 * 1000;
// Scheduled executor service
- private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryReconnectTimer", true));
+ private final ScheduledExecutorService reconnectTimer = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryReconnectTimer", true));
// Reconnection timer, regular check connection is available. If unavailable, unlimited reconnection.
private final ScheduledFuture<?> reconnectFuture;
@@ -59,13 +60,18 @@
private final RegistryService registryService;
+ /**
+ * The time in milliseconds the reconnectTimer will wait
+ */
+ private final int reconnectPeriod;
+
public DubboRegistry(Invoker<RegistryService> registryInvoker, RegistryService registryService) {
super(registryInvoker.getUrl());
this.registryInvoker = registryInvoker;
this.registryService = registryService;
// Start reconnection timer
- int reconnectPeriod = registryInvoker.getUrl().getParameter(Constants.REGISTRY_RECONNECT_PERIOD_KEY, RECONNECT_PERIOD_DEFAULT);
- reconnectFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
+ this.reconnectPeriod = registryInvoker.getUrl().getParameter(Constants.REGISTRY_RECONNECT_PERIOD_KEY, RECONNECT_PERIOD_DEFAULT);
+ reconnectFuture = reconnectTimer.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
// Check and connect to the registry
@@ -127,6 +133,7 @@
logger.warn("Failed to cancel reconnect timer", t);
}
registryInvoker.destroy();
+ ExecutorUtil.gracefulShutdown(reconnectTimer, reconnectPeriod);
}
@Override
diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
index aa499fc..d392839 100644
--- a/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
@@ -21,6 +21,7 @@
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
import com.alibaba.dubbo.common.utils.NamedThreadFactory;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
@@ -314,6 +315,7 @@
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
+ ExecutorUtil.gracefulShutdown(cleanExecutor, cleanPeriod);
}
protected void registered(URL url) {
diff --git a/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java b/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
index ea4b22c..6a99791 100644
--- a/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
+++ b/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
@@ -20,6 +20,7 @@
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
import com.alibaba.dubbo.common.utils.NamedThreadFactory;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.common.utils.UrlUtils;
@@ -263,6 +264,7 @@
logger.warn("Failed to destroy the redis registry client. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
}
}
+ ExecutorUtil.gracefulShutdown(expireExecutor, expirePeriod);
}
@Override
diff --git a/pom.xml b/pom.xml
index 0c94439..c8536ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -136,6 +136,7 @@
<module>dubbo-demo</module>
<module>dubbo-plugin</module>
<module>dubbo-serialization</module>
+ <module>dubbo-bootstrap</module>
<module>dependencies-bom</module>
<module>bom</module>
<module>all</module>