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

albumenj pushed a commit to branch 3.2
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.2 by this push:
     new c65754681c fix issue: resolve the conficts between Dubbo shutdown hook 
and Spring. (#10730)
c65754681c is described below

commit c65754681c779f4490bb9c78ff0a0e9a72a4f7a4
Author: pandaapo <[email protected]>
AuthorDate: Mon Oct 17 10:53:36 2022 +0800

    fix issue: resolve the conficts between Dubbo shutdown hook and Spring. 
(#10730)
---
 .../org/apache/dubbo/rpc/model/ModuleModel.java    |  9 +++
 .../apache/dubbo/rpc/model/ModuleModelTest.java    |  1 +
 .../org/apache/dubbo/config/DubboShutdownHook.java | 44 +++++++++++-
 .../apache/dubbo/config/DubboShutdownHookTest.java | 83 ++++++++++++++++++++++
 .../spring/context/DubboSpringInitializer.java     |  1 +
 5 files changed, 137 insertions(+), 1 deletion(-)

diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java 
b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java
index f94a18f2a9..1af68750f8 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java
@@ -46,6 +46,7 @@ public class ModuleModel extends ScopeModel {
     private ModuleServiceRepository serviceRepository;
     private ModuleConfigManager moduleConfigManager;
     private ModuleDeployer deployer;
+    private boolean lifeCycleManagedExternally = false;
 
     public ModuleModel(ApplicationModel applicationModel) {
         this(applicationModel, false);
@@ -196,4 +197,12 @@ public class ModuleModel extends ScopeModel {
         serviceRepository.registerConsumer(consumerModel);
         return consumerModel;
     }
+
+    public boolean isLifeCycleManagedExternally() {
+        return lifeCycleManagedExternally;
+    }
+
+    public void setLifeCycleManagedExternally(boolean 
lifeCycleManagedExternally) {
+        this.lifeCycleManagedExternally = lifeCycleManagedExternally;
+    }
 }
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleModelTest.java 
b/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleModelTest.java
index f813dfb76c..c1b1c341bc 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleModelTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleModelTest.java
@@ -40,6 +40,7 @@ public class ModuleModelTest {
         Assertions.assertEquals(moduleModel.getApplicationModel(), 
applicationModel);
         
Assertions.assertTrue(applicationModel.getPubModuleModels().contains(moduleModel));
         Assertions.assertNotNull(moduleModel.getInternalId());
+        Assertions.assertFalse(moduleModel.isLifeCycleManagedExternally());
 
         Assertions.assertNotNull(moduleModel.getExtensionDirector());
         Assertions.assertNotNull(moduleModel.getBeanFactory());
diff --git 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
index 6e023d391f..988ad616df 100644
--- 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
@@ -22,7 +22,9 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.Assert;
 import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
 
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_SHUTDOWN_HOOK;
@@ -77,7 +79,47 @@ public class DubboShutdownHook extends Thread {
     }
 
     private void doDestroy() {
-        applicationModel.destroy();
+        boolean hasModuleBindSpring = false;
+        // check if any modules are bound to Spring
+        for (ModuleModel module: applicationModel.getModuleModels()) {
+            if (module.isLifeCycleManagedExternally()) {
+                hasModuleBindSpring = true;
+                break;
+            }
+        }
+        if (hasModuleBindSpring) {
+            int timeout = 
ConfigurationUtils.getServerShutdownTimeout(applicationModel);
+            if (timeout > 0) {
+                long start = System.currentTimeMillis();
+                /**
+                 * To avoid shutdown conflicts between Dubbo and Spring,
+                 * wait for the modules bound to Spring to be handled by 
Spring util timeout.
+                 */
+                logger.info("Waiting for modules managed by Spring to be shut 
down.");
+                while (!applicationModel.isDestroyed() && hasModuleBindSpring
+                    && (System.currentTimeMillis() - start) < timeout) {
+                    try {
+                        TimeUnit.MILLISECONDS.sleep(10);
+                        hasModuleBindSpring = false;
+                        if (!applicationModel.isDestroyed()) {
+                            for (ModuleModel module: 
applicationModel.getModuleModels()) {
+                                if (module.isLifeCycleManagedExternally()) {
+                                    hasModuleBindSpring = true;
+                                    break;
+                                }
+                            }
+                        }
+                    } catch (InterruptedException e) {
+                        logger.warn(e.getMessage(), e);
+                    }
+                }
+            }
+        }
+        if (!applicationModel.isDestroyed()) {
+            logger.info("Dubbo shuts down application " +
+                "after Spring fails to do in time or doesn't do it 
completely.");
+            applicationModel.destroy();
+        }
     }
 
     /**
diff --git 
a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/DubboShutdownHookTest.java
 
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/DubboShutdownHookTest.java
new file mode 100644
index 0000000000..572f1a1216
--- /dev/null
+++ 
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/DubboShutdownHookTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.config;
+
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.TimeUnit;
+
+import static java.util.Arrays.asList;
+
+public class DubboShutdownHookTest {
+    private DubboShutdownHook dubboShutdownHook;
+    private ApplicationModel applicationModel;
+
+    @BeforeEach
+    public void init() {
+        SysProps.setProperty(CommonConstants.IGNORE_LISTEN_SHUTDOWN_HOOK, 
"false");
+        FrameworkModel frameworkModel = new FrameworkModel();
+        applicationModel = new ApplicationModel(frameworkModel);
+        ModuleModel moduleModel = applicationModel.newModule();
+        dubboShutdownHook = new DubboShutdownHook(applicationModel);
+    }
+
+    @AfterEach
+    public void clear() {
+        SysProps.clear();
+    }
+
+    @Test
+    public void testDubboShutdownHook() {
+        Assertions.assertNotNull(dubboShutdownHook);
+        Assertions.assertLinesMatch(asList("DubboShutdownHook"), 
asList(dubboShutdownHook.getName()));
+        Assertions.assertFalse(dubboShutdownHook.getRegistered());
+    }
+
+    @Test
+    public void testDestoryNoModuleManagedExternally() {
+        boolean hasModuleManagedExternally = false;
+        for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
+            if (moduleModel.isLifeCycleManagedExternally()) {
+                hasModuleManagedExternally = true;
+                break;
+            }
+        }
+        Assertions.assertFalse(hasModuleManagedExternally);
+        dubboShutdownHook.run();
+        Assertions.assertTrue(applicationModel.isDestroyed());
+    }
+
+    @Test
+    public void testDestoryWithModuleManagedExternally() throws 
InterruptedException {
+        
applicationModel.getModuleModels().get(0).setLifeCycleManagedExternally(true);
+        new Thread(() -> {
+            applicationModel.getModuleModels().get(0).destroy();
+        }).start();
+        TimeUnit.MILLISECONDS.sleep(10);
+        dubboShutdownHook.run();
+        Assertions.assertTrue(applicationModel.isDestroyed());
+    }
+}
diff --git 
a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializer.java
 
b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializer.java
index 1fcf046854..28f277837c 100644
--- 
a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializer.java
+++ 
b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializer.java
@@ -138,6 +138,7 @@ public class DubboSpringInitializer {
 
         // mark context as bound
         context.markAsBound();
+        moduleModel.setLifeCycleManagedExternally(true);
 
         // register common beans
         DubboBeanUtils.registerCommonBeans(registry);

Reply via email to