Return-Path: X-Original-To: apmail-camel-commits-archive@www.apache.org Delivered-To: apmail-camel-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id CA0CF182F4 for ; Tue, 9 Feb 2016 09:30:38 +0000 (UTC) Received: (qmail 62572 invoked by uid 500); 9 Feb 2016 09:30:38 -0000 Delivered-To: apmail-camel-commits-archive@camel.apache.org Received: (qmail 62517 invoked by uid 500); 9 Feb 2016 09:30:38 -0000 Mailing-List: contact commits-help@camel.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@camel.apache.org Delivered-To: mailing list commits@camel.apache.org Received: (qmail 62504 invoked by uid 99); 9 Feb 2016 09:30:38 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 09 Feb 2016 09:30:38 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 899E1E0099; Tue, 9 Feb 2016 09:30:38 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: davsclaus@apache.org To: commits@camel.apache.org Date: Tue, 09 Feb 2016 09:30:38 -0000 Message-Id: <2704f382da9b49e2938a9f16805b6257@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] camel git commit: CAMEL-9431: Fixed spring-boot to not handle lifecycle of beans that Camel is handling, this ensure a proper startup/shutdown sequence. Repository: camel Updated Branches: refs/heads/camel-2.16.x 61f0e6171 -> ec6715020 refs/heads/master 4abfa2fde -> 4af2c3c34 CAMEL-9431: Fixed spring-boot to not handle lifecycle of beans that Camel is handling, this ensure a proper startup/shutdown sequence. Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/4af2c3c3 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/4af2c3c3 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/4af2c3c3 Branch: refs/heads/master Commit: 4af2c3c34bfee00e58632a3b1dc6e299cfd43661 Parents: 4abfa2f Author: Claus Ibsen Authored: Tue Feb 9 10:30:08 2016 +0100 Committer: Claus Ibsen Committed: Tue Feb 9 10:30:08 2016 +0100 ---------------------------------------------------------------------- .../spring/boot/CamelAutoConfiguration.java | 22 +++- .../boot/TypeConversionConfiguration.java | 6 +- .../boot/CamelSpringBootShutdownTest.java | 127 +++++++++++++++++++ 3 files changed, 146 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/4af2c3c3/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java index b3c58ab..e9e81d6 100644 --- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java +++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java @@ -75,7 +75,8 @@ public class CamelAutoConfiguration { /** * Default producer template for the bootstrapped Camel context. */ - @Bean + @Bean(initMethod = "", destroyMethod = "") + // Camel handles the lifecycle of this bean @ConditionalOnMissingBean(ProducerTemplate.class) ProducerTemplate producerTemplate(CamelContext camelContext, CamelConfigurationProperties configurationProperties) { @@ -85,7 +86,8 @@ public class CamelAutoConfiguration { /** * Default consumer template for the bootstrapped Camel context. */ - @Bean + @Bean(initMethod = "", destroyMethod = "") + // Camel handles the lifecycle of this bean @ConditionalOnMissingBean(ConsumerTemplate.class) ConsumerTemplate consumerTemplate(CamelContext camelContext, CamelConfigurationProperties configurationProperties) { @@ -99,11 +101,17 @@ public class CamelAutoConfiguration { return new SpringPropertiesParser(); } - @Bean - PropertiesComponent properties() { - PropertiesComponent properties = new PropertiesComponent(); - properties.setPropertiesParser(propertiesParser()); - return properties; + @Bean(initMethod = "", destroyMethod = "") + // Camel handles the lifecycle of this bean + PropertiesComponent properties(CamelContext camelContext, PropertiesParser parser) { + if (camelContext.hasComponent("properties") != null) { + return camelContext.getComponent("properties", PropertiesComponent.class); + } else { + PropertiesComponent pc = new PropertiesComponent(); + pc.setPropertiesParser(parser); + camelContext.addComponent("properties", pc); + return pc; + } } /** http://git-wip-us.apache.org/repos/asf/camel/blob/4af2c3c3/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/TypeConversionConfiguration.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/TypeConversionConfiguration.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/TypeConversionConfiguration.java index ecb6099..b328f5d 100644 --- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/TypeConversionConfiguration.java +++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/TypeConversionConfiguration.java @@ -33,12 +33,14 @@ import org.springframework.core.convert.support.DefaultConversionService; @ConditionalOnProperty(value = "camel.springboot.typeConversion", matchIfMissing = true) public class TypeConversionConfiguration { - @Bean + @Bean(initMethod = "", destroyMethod = "") + // Camel handles the lifecycle of this bean TypeConverter typeConverter(CamelContext camelContext) { return camelContext.getTypeConverter(); } - @Bean + @Bean(initMethod = "", destroyMethod = "") + // Camel handles the lifecycle of this bean SpringTypeConverter springTypeConverter(CamelContext camelContext, ConversionService[] conversionServices) { SpringTypeConverter springTypeConverter = new SpringTypeConverter(asList(conversionServices)); camelContext.getTypeConverterRegistry().addFallbackTypeConverter(springTypeConverter, true); http://git-wip-us.apache.org/repos/asf/camel/blob/4af2c3c3/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/CamelSpringBootShutdownTest.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/CamelSpringBootShutdownTest.java b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/CamelSpringBootShutdownTest.java new file mode 100644 index 0000000..f2a8611 --- /dev/null +++ b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/CamelSpringBootShutdownTest.java @@ -0,0 +1,127 @@ +/** + * 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.camel.spring.boot; + +import java.io.Closeable; +import java.io.InputStream; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelExecutionException; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.TypeConverter; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.converter.DefaultTypeConverter; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Test class illustrating the invalid shutdown sequence when using the autoconfiguration + * provided by camel-spring-boot. + *

+ * This is caused by the {@link TypeConversionConfiguration} class registering a + * {@link TypeConverter} (of actual type {@link DefaultTypeConverter}) in the Spring + * {@link ApplicationContext}. Its '{@code public void shutdown()}' method is inferred as a destroy-method by Spring, + * which will thus be called before the {@link CamelContext} shutdown + * when the context is closed. + *

+ * As a consequence, any inflight message that should be processed during the graceful + * shutdown period of Camel won't have access to any type conversion support. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) +// Let the CamelAutoConfiguration do all the configuration for us +// including the TypeConverter registration into the ApplicationContext +@SpringApplicationConfiguration({CamelAutoConfiguration.class, CamelSpringBootShutdownTest.TestRouteConfiguration.class}) +public class CamelSpringBootShutdownTest { + + @Autowired + private ConfigurableApplicationContext context; + + @Autowired + private ProducerTemplate template; + + @Test + public void test1() throws Exception { + try { + // Send a String body that need to be converted to an InputStream + template.sendBody("direct:start", "42"); + } catch (CamelExecutionException e) { + // unwrap Exception + throw (Exception) e.getCause(); + } + } + + @Test + public void test2() throws Exception { + try { + // Starts a Thread to close the context in 500 ms + new DelayedCloser(context, 500).start(); + // Send the same body, and let the context be closed before the processing happens + template.sendBody("direct:start", "42"); + } catch (CamelExecutionException e) { + // unwrap Exception + throw (Exception) e.getCause(); + } + } + + public static class DelayedCloser extends Thread { + + private final long sleep; + private final Closeable closeable; + + public DelayedCloser(Closeable closeable, long sleep) { + this.closeable = closeable; + this.sleep = sleep; + } + + @Override + public void run() { + try { + Thread.sleep(sleep); + closeable.close(); + } catch (Exception e) { + // ignore + } + } + } + + public static class TestRouteConfiguration { + @Bean + public RouteBuilder route() { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + // delay the processing to force the exchange to be inflight + // during the context shutdown + .delay(1000) + .convertBodyTo(InputStream.class) + .to("log:route-log"); + } + }; + } + } + +} \ No newline at end of file