From 595b04db6f862201a08b045c26ea00d01edca855 Mon Sep 17 00:00:00 2001 From: beiwei30 Date: Thu, 1 Nov 2018 17:39:44 +0800 Subject: [PATCH 1/2] #2525: Problems of graceful shutdown in 2.6.3 and some recommendations --- dubbo-all/pom.xml | 8 -- dubbo-bom/pom.xml | 5 - dubbo-bootstrap/pom.xml | 47 -------- .../dubbo/bootstrap/DubboBootstrap.java | 111 ------------------ .../apache/dubbo/config/ProtocolConfig.java | 11 +- dubbo-config/dubbo-config-spring/pom.xml | 7 +- .../dubbo/config/spring/ServiceBean.java | 25 +--- .../extension/SpringExtensionFactory.java | 18 +++ .../DubboApplicationContextInitializer.java | 39 ------ .../initializer/DubboApplicationListener.java | 49 -------- .../initializer/DubboContextListener.java | 72 ------------ .../config/spring/util/BeanFactoryUtils.java | 27 +++++ .../main/resources/META-INF/web-fragment.xml | 22 ---- ...ubboApplicationContextInitializerTest.java | 88 -------------- .../DubboApplicationListenerTest.java | 59 ---------- .../dubbo-container-spring/pom.xml | 7 +- .../container/spring/SpringContainer.java | 6 +- dubbo-distribution/pom.xml | 5 - pom.xml | 1 - 19 files changed, 53 insertions(+), 554 deletions(-) delete mode 100644 dubbo-bootstrap/pom.xml delete mode 100644 dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java delete mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboApplicationContextInitializer.java delete mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboApplicationListener.java delete mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboContextListener.java delete mode 100644 dubbo-config/dubbo-config-spring/src/main/resources/META-INF/web-fragment.xml delete mode 100644 dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/initializer/DubboApplicationContextInitializerTest.java delete mode 100644 dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/initializer/DubboApplicationListenerTest.java diff --git a/dubbo-all/pom.xml b/dubbo-all/pom.xml index 8e8fa661fb5..12d9ec9efd3 100644 --- a/dubbo-all/pom.xml +++ b/dubbo-all/pom.xml @@ -325,13 +325,6 @@ compile true - - org.apache.dubbo - dubbo-bootstrap - ${project.version} - compile - true - org.apache.dubbo dubbo-compatible @@ -439,7 +432,6 @@ org.apache.dubbo:dubbo-serialization-kryo org.apache.dubbo:dubbo-serialization-jdk org.apache.dubbo:dubbo-serialization-protostuff - org.apache.dubbo:dubbo-bootstrap diff --git a/dubbo-bom/pom.xml b/dubbo-bom/pom.xml index 15c8ae84b5a..dac2456d830 100644 --- a/dubbo-bom/pom.xml +++ b/dubbo-bom/pom.xml @@ -283,11 +283,6 @@ dubbo-serialization-protostuff ${project.version} - - org.apache.dubbo - dubbo-bootstrap - ${project.version} - org.apache.dubbo dubbo-compatible diff --git a/dubbo-bootstrap/pom.xml b/dubbo-bootstrap/pom.xml deleted file mode 100644 index a082550b362..00000000000 --- a/dubbo-bootstrap/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - dubbo-parent - org.apache.dubbo - 2.7.0-SNAPSHOT - - 4.0.0 - - dubbo-bootstrap - - - - - org.apache.dubbo - dubbo-config-api - ${project.parent.version} - - - org.apache.dubbo - dubbo-common - ${project.parent.version} - - - org.apache.dubbo - dubbo-registry-api - ${project.parent.version} - - - \ 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 deleted file mode 100644 index b896c0ee847..00000000000 --- a/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 org.apache.dubbo.config.DubboShutdownHook; -import org.apache.dubbo.config.ServiceConfig; - -import java.util.ArrayList; -import java.util.List; - -/** - * 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 { - - /** - * The list of ServiceConfig - */ - private List serviceConfigList; - - /** - * Whether register the shutdown hook during start? - */ - private final boolean registerShutdownHookOnStart; - - /** - * The shutdown hook used when Dubbo is running under embedded environment - */ - private DubboShutdownHook shutdownHook; - - public DubboBootstrap() { - this(true, DubboShutdownHook.getDubboShutdownHook()); - } - - public DubboBootstrap(boolean registerShutdownHookOnStart) { - this(registerShutdownHookOnStart, DubboShutdownHook.getDubboShutdownHook()); - } - - public DubboBootstrap(boolean registerShutdownHookOnStart, DubboShutdownHook shutdownHook) { - this.serviceConfigList = new ArrayList(); - this.shutdownHook = shutdownHook; - this.registerShutdownHookOnStart = registerShutdownHookOnStart; - } - - /** - * Register service config to bootstrap, which will be called during {@link DubboBootstrap#stop()} - * @param serviceConfig the service - * @return the bootstrap instance - */ - public DubboBootstrap registerServiceConfig(ServiceConfig serviceConfig) { - serviceConfigList.add(serviceConfig); - return this; - } - - public void start() { - if (registerShutdownHookOnStart) { - registerShutdownHook(); - } else { - // DubboShutdown hook has been registered in AbstractConfig, - // we need to remove it explicitly - removeShutdownHook(); - } - for (ServiceConfig serviceConfig: serviceConfigList) { - serviceConfig.export(); - } - } - - public void stop() { - for (ServiceConfig serviceConfig: serviceConfigList) { - serviceConfig.unexport(); - } - shutdownHook.destroyAll(); - if (registerShutdownHookOnStart) { - 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 - } - } -} diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ProtocolConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ProtocolConfig.java index 3ae33291adc..75821690a1f 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ProtocolConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ProtocolConfig.java @@ -470,13 +470,4 @@ public void destroy() { ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).destroy(); } } - - /** - * Just for compatibility. - * It should be deleted in the next major version, say 2.7.x. - */ - @Deprecated - public static void destroyAll() { - DubboShutdownHook.getDubboShutdownHook().destroyAll(); - } -} \ No newline at end of file +} diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml index 995209db45e..626556cc324 100644 --- a/dubbo-config/dubbo-config-spring/pom.xml +++ b/dubbo-config/dubbo-config-spring/pom.xml @@ -34,11 +34,6 @@ dubbo-config-api ${project.parent.version} - - org.apache.dubbo - dubbo-bootstrap - ${project.parent.version} - org.springframework spring-beans @@ -157,4 +152,4 @@ --> - \ No newline at end of file + diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java index 6d7bc9b6476..c14ccb7f336 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java @@ -34,13 +34,13 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.support.AbstractApplicationContext; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; +import static org.apache.dubbo.config.spring.util.BeanFactoryUtils.addApplicationListener; + /** * ServiceFactoryBean * @@ -72,23 +72,7 @@ public ServiceBean(Service service) { public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; SpringExtensionFactory.addApplicationContext(applicationContext); - try { - Method method = applicationContext.getClass().getMethod("addApplicationListener", ApplicationListener.class); // backward compatibility to spring 2.0.1 - method.invoke(applicationContext, this); - supportedApplicationListener = true; - } catch (Throwable t) { - if (applicationContext instanceof AbstractApplicationContext) { - try { - Method method = AbstractApplicationContext.class.getDeclaredMethod("addListener", ApplicationListener.class); // backward compatibility to spring 2.0.1 - if (!method.isAccessible()) { - method.setAccessible(true); - } - method.invoke(applicationContext, this); - supportedApplicationListener = true; - } catch (Throwable t2) { - } - } - } + supportedApplicationListener = addApplicationListener(applicationContext, this); } @Override @@ -250,8 +234,7 @@ && getInterface() != null && getInterface().length() > 0 @Override public void destroy() throws Exception { // This will only be called for singleton scope bean, and expected to be called by spring shutdown hook when BeanFactory/ApplicationContext destroys. - // We will guarantee dubbo related resources being released with dubbo shutdown hook. - //unexport(); + unexport(); } // merged from dubbox diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java index 56862f33b4a..546c9c36db8 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java @@ -17,14 +17,20 @@ package org.apache.dubbo.config.spring.extension; import java.util.Set; + import org.apache.dubbo.common.extension.ExtensionFactory; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.config.DubboShutdownHook; +import org.apache.dubbo.config.spring.util.BeanFactoryUtils; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; /** * SpringExtensionFactory @@ -33,9 +39,11 @@ public class SpringExtensionFactory implements ExtensionFactory { private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class); private static final Set contexts = new ConcurrentHashSet(); + private static final ApplicationListener shutdownHookListener = new ShutdownHookListener(); public static void addApplicationContext(ApplicationContext context) { contexts.add(context); + BeanFactoryUtils.addApplicationListener(context, shutdownHookListener); } public static void removeApplicationContext(ApplicationContext context) { @@ -88,4 +96,14 @@ public T getExtension(Class type, String name) { return null; } + private static class ShutdownHookListener implements ApplicationListener { + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ContextClosedEvent) { + DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook(); + Runtime.getRuntime().removeShutdownHook(shutdownHook); + shutdownHook.destroyAll(); + } + } + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboApplicationContextInitializer.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboApplicationContextInitializer.java deleted file mode 100644 index 36727e669f4..00000000000 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboApplicationContextInitializer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.spring.initializer; - -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; - -/** - * Automatically register {@link DubboApplicationListener} to Spring context - * A {@link org.springframework.web.context.ContextLoaderListener} class is defined in - * src/main/resources/META-INF/web-fragment.xml - * In the web-fragment.xml, {@link DubboApplicationContextInitializer} is defined in context params. - * This file will be discovered if running under a servlet 3.0+ container. - * Even if user specifies {@link org.springframework.web.context.ContextLoaderListener} in web.xml, - * it will be merged to web.xml. - * If user specifies in web.xml, this will no take effect, - * unless user configures {@link DubboApplicationContextInitializer} explicitly in web.xml. - */ -public class DubboApplicationContextInitializer implements ApplicationContextInitializer { - - @Override - public void initialize(ConfigurableApplicationContext applicationContext) { - applicationContext.addApplicationListener(new DubboApplicationListener()); - } -} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboApplicationListener.java deleted file mode 100644 index 8b6409bd65f..00000000000 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboApplicationListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.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 { - - private DubboBootstrap dubboBootstrap; - - public DubboApplicationListener() { - dubboBootstrap = new DubboBootstrap(false); - } - - public DubboApplicationListener(DubboBootstrap dubboBootstrap) { - this.dubboBootstrap = 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/org/apache/dubbo/config/spring/initializer/DubboContextListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboContextListener.java deleted file mode 100644 index 35b2b70c2c2..00000000000 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboContextListener.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.spring.initializer; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import javax.servlet.ServletContext; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; - -/** - * A Dubbo context listener is a delegation to org.springframework.web.context.ContextLoaderListener. This is necessary, - * because Dubbo is packaged into all-in-one jar, therefore it contains a web-fragment.xml from this sub module which's - * used for helping to assemble spring context listener automatically when it's not configured explicitly by user in - * web.xml. It works fine with spring, but it will lead to ClassNotFound exception and fail tomcat's bootup when user - * doesn't depend on spring framework. - */ -public class DubboContextListener implements ServletContextListener { - private static final Log logger = LogFactory.getLog(DubboContextListener.class); - - private static final String SPRING_CONTEXT_LISTENER = "org.springframework.web.context.ContextLoaderListener"; - private static final String SPRING_CONTEXT_ROOT = "org.springframework.web.context.WebApplicationContext.ROOT"; - - private ServletContextListener springContextListener; - private boolean executed = false; - - public DubboContextListener() { - try { - Class c = Class.forName(SPRING_CONTEXT_LISTENER); - springContextListener = (ServletContextListener) c.newInstance(); - } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { - logger.warn("Servlet container detects dubbo's web fragment configuration, and tries to load " + - "org.springframework.web.context.ContextLoaderListener but fails to find the class. " + - "If the application don't rely on Spring framework, pls. simply ignore"); - } - } - - @Override - public void contextInitialized(ServletContextEvent servletContextEvent) { - if (springContextListener != null) { - // if spring context listener has already been registered, then do nothing - ServletContext context = servletContextEvent.getServletContext(); - if (context.getAttribute(SPRING_CONTEXT_ROOT) == null) { - executed = true; - springContextListener.contextInitialized(servletContextEvent); - } - } - } - - @Override - public void contextDestroyed(ServletContextEvent servletContextEvent) { - if (springContextListener != null && executed) { - springContextListener.contextDestroyed(servletContextEvent); - } - } -} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/BeanFactoryUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/BeanFactoryUtils.java index e78739917dc..e9b170885c3 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/BeanFactoryUtils.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/BeanFactoryUtils.java @@ -20,7 +20,11 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.support.AbstractApplicationContext; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -89,4 +93,27 @@ public static List getBeans(ListableBeanFactory beanFactory, String[] bea } + public static boolean addApplicationListener(ApplicationContext applicationContext, ApplicationListener listener) { + try { + // backward compatibility to spring 2.0.1 + Method method = applicationContext.getClass().getMethod("addApplicationListener", ApplicationListener.class); + method.invoke(applicationContext, listener); + return true; + } catch (Throwable t) { + if (applicationContext instanceof AbstractApplicationContext) { + try { + // backward compatibility to spring 2.0.1 + Method method = AbstractApplicationContext.class.getDeclaredMethod("addListener", ApplicationListener.class); + if (!method.isAccessible()) { + method.setAccessible(true); + } + method.invoke(applicationContext, listener); + return true; + } catch (Throwable t2) { + // ignore + } + } + } + return false; + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/web-fragment.xml b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/web-fragment.xml deleted file mode 100644 index e1eef6ba0de..00000000000 --- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/web-fragment.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - dubbo_fragment - - - - - - - - - contextInitializerClasses - org.apache.dubbo.config.spring.initializer.DubboApplicationContextInitializer - - - - org.apache.dubbo.config.spring.initializer.DubboContextListener - - - diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/initializer/DubboApplicationContextInitializerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/initializer/DubboApplicationContextInitializerTest.java deleted file mode 100644 index 02dda03a7ac..00000000000 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/initializer/DubboApplicationContextInitializerTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.spring.initializer; - -import org.apache.catalina.core.StandardContext; -import org.apache.catalina.startup.ContextConfig; -import org.apache.catalina.startup.Tomcat; -import org.junit.Assert; -import org.junit.Test; -import org.springframework.web.context.ContextLoaderListener; - - -public class DubboApplicationContextInitializerTest { - - @Test - public void testSpringContextLoaderListenerInWebXml() throws Exception { - Tomcat tomcat = new Tomcat(); - tomcat.setBaseDir("target/test-classes"); - tomcat.setPort(12345); - StandardContext context = new StandardContext(); - context.setName("test"); - context.setDocBase("test"); - context.setPath("/test"); - context.addLifecycleListener(new ContextConfig()); - tomcat.getHost().addChild(context); - tomcat.start(); - // there should be 2 application listeners, one is spring context listener, - // the other is its wrapper dubbo introduces. - Assert.assertEquals(2, context.getApplicationLifecycleListeners().length); - // the first one should be Spring's built in ContextLoaderListener. - Assert.assertTrue(context.getApplicationLifecycleListeners()[0] instanceof ContextLoaderListener); - tomcat.stop(); - tomcat.destroy(); - } - - @Test - public void testNoListenerInWebXml() throws Exception { - Tomcat tomcat = new Tomcat(); - tomcat.setBaseDir("target/test-classes"); - tomcat.setPort(12345); - StandardContext context = new StandardContext(); - context.setName("test2"); - context.setDocBase("test2"); - context.setPath("/test2"); - context.addLifecycleListener(new ContextConfig()); - tomcat.getHost().addChild(context); - tomcat.start(); - // there should be 1 application listener, which is spring context listener's wrapper introduced by dubbo - Assert.assertEquals(1, context.getApplicationLifecycleListeners().length); - // the first one should be Spring's built in ContextLoaderListener. - Assert.assertTrue(context.getApplicationLifecycleListeners()[0] instanceof DubboContextListener); - tomcat.stop(); - tomcat.destroy(); - } - - @Test - public void testMetadataComplete() throws Exception { - Tomcat tomcat = new Tomcat(); - tomcat.setBaseDir("target/test-classes"); - tomcat.setPort(12345); - StandardContext context = new StandardContext(); - context.setName("test3"); - context.setDocBase("test3"); - context.setPath("/test3"); - context.addLifecycleListener(new ContextConfig()); - tomcat.getHost().addChild(context); - tomcat.start(); - // there should be no application listeners - Assert.assertEquals(0, context.getApplicationLifecycleListeners().length); - tomcat.stop(); - tomcat.destroy(); - } - -} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/initializer/DubboApplicationListenerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/initializer/DubboApplicationListenerTest.java deleted file mode 100644 index 9b953d5ba70..00000000000 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/initializer/DubboApplicationListenerTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.spring.initializer; - -import org.apache.dubbo.config.DubboShutdownHook; -import org.apache.dubbo.bootstrap.DubboBootstrap; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -public class DubboApplicationListenerTest { - - @Test - public void testTwoShutdownHook() { - DubboShutdownHook spyHook = Mockito.spy(DubboShutdownHook.getDubboShutdownHook()); - ClassPathXmlApplicationContext applicationContext = getApplicationContext(spyHook, true); - applicationContext.refresh(); - applicationContext.close(); - // shutdown hook can't be verified, because it will executed after main thread has finished. - // so we can only verify it by manually run it. - try { - spyHook.start(); - spyHook.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - Mockito.verify(spyHook, Mockito.times(2)).destroyAll(); - } - - @Test - public void testOneShutdownHook() { - DubboShutdownHook spyHook = Mockito.spy(DubboShutdownHook.getDubboShutdownHook()); - ClassPathXmlApplicationContext applicationContext = getApplicationContext(spyHook, false); - applicationContext.refresh(); - applicationContext.close(); - Mockito.verify(spyHook, Mockito.times(1)).destroyAll(); - } - - private ClassPathXmlApplicationContext getApplicationContext(DubboShutdownHook hook, boolean registerHook) { - DubboBootstrap bootstrap = new DubboBootstrap(registerHook, hook); - ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(); - applicationContext.addApplicationListener(new DubboApplicationListener(bootstrap)); - return applicationContext; - } -} diff --git a/dubbo-container/dubbo-container-spring/pom.xml b/dubbo-container/dubbo-container-spring/pom.xml index cbcc43e5115..987b2c9d2e6 100644 --- a/dubbo-container/dubbo-container-spring/pom.xml +++ b/dubbo-container/dubbo-container-spring/pom.xml @@ -38,10 +38,5 @@ org.springframework spring-context - - org.apache.dubbo - dubbo-config-spring - ${project.parent.version} - - \ No newline at end of file + diff --git a/dubbo-container/dubbo-container-spring/src/main/java/org/apache/dubbo/container/spring/SpringContainer.java b/dubbo-container/dubbo-container-spring/src/main/java/org/apache/dubbo/container/spring/SpringContainer.java index 9aa9ca5c83c..c6ec4746731 100644 --- a/dubbo-container/dubbo-container-spring/src/main/java/org/apache/dubbo/container/spring/SpringContainer.java +++ b/dubbo-container/dubbo-container-spring/src/main/java/org/apache/dubbo/container/spring/SpringContainer.java @@ -19,7 +19,6 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConfigUtils; -import org.apache.dubbo.config.spring.initializer.DubboApplicationListener; import org.apache.dubbo.container.Container; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -44,10 +43,7 @@ public void start() { if (configPath == null || configPath.length() == 0) { configPath = DEFAULT_SPRING_CONFIG; } - context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"), false); - context.addApplicationListener(new DubboApplicationListener()); - context.registerShutdownHook(); - context.refresh(); + context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+")); context.start(); } diff --git a/dubbo-distribution/pom.xml b/dubbo-distribution/pom.xml index 4611320fab7..c5c6f0a2af6 100644 --- a/dubbo-distribution/pom.xml +++ b/dubbo-distribution/pom.xml @@ -250,11 +250,6 @@ dubbo-compatible ${project.version} - - org.apache.dubbo - dubbo-bootstrap - ${project.version} - com.alibaba hessian-lite diff --git a/pom.xml b/pom.xml index 58825574b33..8f3c0e5a762 100644 --- a/pom.xml +++ b/pom.xml @@ -139,7 +139,6 @@ dubbo-demo dubbo-plugin dubbo-serialization - dubbo-bootstrap dubbo-compatible dubbo-dependencies-bom dubbo-bom From d6572aa5a3819a637902e7f9474b6a8477410f53 Mon Sep 17 00:00:00 2001 From: beiwei30 Date: Fri, 2 Nov 2018 21:39:20 +0800 Subject: [PATCH 2/2] make sure shutdown hook get invoked correctly, no matter spring is present or not --- .../dubbo/config/spring/AnnotationBean.java | 23 +++++++++---------- .../dubbo/config/spring/ServiceBean.java | 3 +-- .../extension/SpringExtensionFactory.java | 4 +++- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/AnnotationBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/AnnotationBean.java index 7612c3302a3..6b54a395db8 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/AnnotationBean.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/AnnotationBean.java @@ -28,7 +28,6 @@ import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; -import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.annotation.Reference; @@ -111,17 +110,16 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) @Override public void destroy() { - - // This will only be called for singleton scope bean, and expected to be called by spring shutdown hook when BeanFactory/ApplicationContext destroys. - // We will guarantee dubbo related resources being released with dubbo shutdown hook. - - // for (ServiceConfig serviceConfig : serviceConfigs) { - // try { - // serviceConfig.unexport(); - // } catch (Throwable e) { - // logger.error(e.getMessage(), e); - // } - // } + // no need to destroy here + // see org.apache.dubbo.config.spring.extension.SpringExtensionFactory.ShutdownHookListener + /* + for (ServiceConfig serviceConfig : serviceConfigs) { + try { + serviceConfig.unexport(); + } catch (Throwable e) { + logger.error(e.getMessage(), e); + } + } for (ReferenceConfig referenceConfig : referenceConfigs.values()) { try { @@ -130,6 +128,7 @@ public void destroy() { logger.error(e.getMessage(), e); } } + */ } @Override diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java index c14ccb7f336..19dcef17a40 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java @@ -233,8 +233,7 @@ && getInterface() != null && getInterface().length() > 0 @Override public void destroy() throws Exception { - // This will only be called for singleton scope bean, and expected to be called by spring shutdown hook when BeanFactory/ApplicationContext destroys. - unexport(); + // no need to export here, see org.apache.dubbo.config.spring.extension.SpringExtensionFactory.ShutdownHookListener } // merged from dubbox diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java index 546c9c36db8..668476b556b 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java @@ -100,8 +100,10 @@ private static class ShutdownHookListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextClosedEvent) { + // we call it anyway since dubbo shutdown hook make sure its destroyAll() is re-entrant. + // pls. note we should not remove dubbo shutdown hook when spring framework is present, this is because + // its shutdown hook may not be installed. DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook(); - Runtime.getRuntime().removeShutdownHook(shutdownHook); shutdownHook.destroyAll(); } }