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 new file mode 100644 index 00000000000..35b2b70c2c2 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/initializer/DubboContextListener.java @@ -0,0 +1,72 @@ +/* + * 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/resources/META-INF/web-fragment.xml b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/web-fragment.xml index c063bdf2f37..e1eef6ba0de 100644 --- 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 @@ -16,7 +16,7 @@ - org.springframework.web.context.ContextLoaderListener + org.apache.dubbo.config.spring.initializer.DubboContextListener - \ No newline at end of file + 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 index b5b8f8c9a9b..02dda03a7ac 100644 --- 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 @@ -38,8 +38,9 @@ public void testSpringContextLoaderListenerInWebXml() throws Exception { context.addLifecycleListener(new ContextConfig()); tomcat.getHost().addChild(context); tomcat.start(); - // there should be 1 application listener - Assert.assertEquals(1, context.getApplicationLifecycleListeners().length); + // 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(); @@ -58,10 +59,10 @@ public void testNoListenerInWebXml() throws Exception { context.addLifecycleListener(new ContextConfig()); tomcat.getHost().addChild(context); tomcat.start(); - // there should be 1 application listener + // 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 ContextLoaderListener); + Assert.assertTrue(context.getApplicationLifecycleListeners()[0] instanceof DubboContextListener); tomcat.stop(); tomcat.destroy(); } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/DataSourceStatusCheckerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/DataSourceStatusCheckerTest.java index 532d97b5473..c82fd4af615 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/DataSourceStatusCheckerTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/DataSourceStatusCheckerTest.java @@ -49,6 +49,7 @@ public class DataSourceStatusCheckerTest { @Before public void setUp() throws Exception { + SpringExtensionFactory.clearContexts(); initMocks(this); this.dataSourceStatusChecker = new DataSourceStatusChecker(); new ServiceBean().setApplicationContext(applicationContext); @@ -56,7 +57,6 @@ public void setUp() throws Exception { @After public void tearDown() throws Exception { - SpringExtensionFactory.clearContexts(); Mockito.reset(applicationContext); }