You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2017/08/09 10:05:25 UTC

camel git commit: CAMEL-11579: Add unit test / example for SupervisingRouteController

Repository: camel
Updated Branches:
  refs/heads/master 57299969c -> a613a589e


CAMEL-11579: Add unit test / example for SupervisingRouteController


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/a613a589
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/a613a589
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/a613a589

Branch: refs/heads/master
Commit: a613a589e9f15062f973dd426aca8d490b308398
Parents: 5729996
Author: lburgazzoli <lb...@gmail.com>
Authored: Tue Aug 8 14:56:21 2017 +0200
Committer: lburgazzoli <lb...@gmail.com>
Committed: Wed Aug 9 12:04:47 2017 +0200

----------------------------------------------------------------------
 .../org/apache/camel/spi/RouteController.java   |  16 ++
 .../camel/util/backoff/BackOffContext.java      |   7 +-
 .../apache/camel/util/backoff/BackOffTimer.java |  13 +-
 .../SupervisingRouteControllerRestartTest.java  | 146 +++++++++++++++++++
 .../boot/SupervisingRouteControllerTest.java    | 126 ++++++++++++++++
 5 files changed, 300 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/a613a589/camel-core/src/main/java/org/apache/camel/spi/RouteController.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/spi/RouteController.java b/camel-core/src/main/java/org/apache/camel/spi/RouteController.java
index d19a8a2..b772a7c 100644
--- a/camel-core/src/main/java/org/apache/camel/spi/RouteController.java
+++ b/camel-core/src/main/java/org/apache/camel/spi/RouteController.java
@@ -46,4 +46,20 @@ public interface RouteController extends CamelContextAware, Service {
     void suspendRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception;
 
     void resumeRoute(String routeId) throws Exception;
+
+    /**
+     * Access the underlying concrete RouteController implementation.
+     *
+     * @param clazz the proprietary class or interface of the underlying concrete RouteController.
+     * @return an instance of the underlying concrete RouteController as the required type.
+     */
+    default <T extends RouteController> T unwrap(Class<T> clazz) {
+        if (RouteController.class.isAssignableFrom(clazz)) {
+            return clazz.cast(this);
+        }
+
+        throw new IllegalArgumentException(
+            "Unable to unwrap this RouteController type (" + getClass() + ") to the required type (" + clazz + ")"
+        );
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/a613a589/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffContext.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffContext.java b/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffContext.java
index cd46653..617f0a8 100644
--- a/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffContext.java
+++ b/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffContext.java
@@ -28,6 +28,7 @@ public final class BackOffContext {
 
     public BackOffContext(BackOff backOff) {
         this.backOff = backOff;
+
         this.currentAttempts = 0;
         this.currentDelay = backOff.getDelay().toMillis();
         this.currentElapsedTime = 0;
@@ -40,7 +41,7 @@ public final class BackOffContext {
     /**
      * The back-off associated with this context.
      */
-    public BackOff backOff() {
+    public BackOff getBackOff() {
         return backOff;
     }
 
@@ -81,7 +82,7 @@ public final class BackOffContext {
      * or ${@link BackOff#NEVER} to indicate that no further attempt should be
      * made.
      */
-    public long next() {
+    long next() {
         // A call to next when currentDelay is set to NEVER has no effects
         // as this means that either the timer is exhausted or it has explicit
         // stopped
@@ -95,7 +96,7 @@ public final class BackOffContext {
                 currentDelay = BackOff.NEVER;
             } else {
                 if (currentDelay <= backOff.getMaxDelay().toMillis()) {
-                    currentDelay = (long) (currentDelay * backOff().getMultiplier());
+                    currentDelay = (long) (currentDelay * backOff.getMultiplier());
                 }
 
                 currentElapsedTime += currentDelay;

http://git-wip-us.apache.org/repos/asf/camel/blob/a613a589/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffTimer.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffTimer.java b/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffTimer.java
index c0c035f..dbae257 100644
--- a/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffTimer.java
+++ b/camel-core/src/main/java/org/apache/camel/util/backoff/BackOffTimer.java
@@ -38,10 +38,9 @@ public class BackOffTimer {
      * according to the given backOff.
      */
     public CompletableFuture<BackOffContext> schedule(BackOff backOff, ThrowingFunction<BackOffContext, Boolean, Exception> function) {
-        final BackOffContext context = new BackOffContext(backOff);
-        final Task task = new Task(context, function);
+        final Task task = new Task(backOff, function);
 
-        long delay = context.next();
+        long delay = task.getContext().next();
         if (delay != BackOff.NEVER) {
             scheduler.schedule(task, delay, TimeUnit.MILLISECONDS);
         } else {
@@ -59,8 +58,8 @@ public class BackOffTimer {
         private final BackOffContext context;
         private final ThrowingFunction<BackOffContext, Boolean, Exception> function;
 
-        Task(BackOffContext context, ThrowingFunction<BackOffContext, Boolean, Exception> function) {
-            this.context = context;
+        Task(BackOff backOff, ThrowingFunction<BackOffContext, Boolean, Exception> function) {
+            this.context = new BackOffContext(backOff);
             this.function = function;
         }
 
@@ -100,5 +99,9 @@ public class BackOffTimer {
         boolean complete() {
             return super.complete(context);
         }
+
+        BackOffContext getContext() {
+            return context;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/a613a589/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerRestartTest.java
----------------------------------------------------------------------
diff --git a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerRestartTest.java b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerRestartTest.java
new file mode 100644
index 0000000..74c8f09
--- /dev/null
+++ b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerRestartTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.net.BindException;
+import java.net.ServerSocket;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ServiceStatus;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.SupervisingRouteController;
+import org.apache.camel.test.AvailablePortFinder;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+    classes = {
+        CamelAutoConfiguration.class,
+        SupervisingRouteControllerAutoConfiguration.class,
+        SupervisingRouteControllerRestartTest.TestConfiguration.class
+    },
+    properties = {
+        "camel.springboot.xml-routes = false",
+        "camel.springboot.main-run-controller = true",
+        "camel.supervising.controller.enabled = true",
+        "camel.supervising.controller.initial-delay = 2s",
+        "camel.supervising.controller.default-back-off.delay = 1s",
+        "camel.supervising.controller.default-back-off.max-attempts = 10",
+        "camel.supervising.controller.routes.bar.back-off.delay = 10s",
+        "camel.supervising.controller.routes.bar.back-off.max-attempts = 3",
+        "camel.supervising.controller.routes.timer-unmanaged.supervise = false"
+    }
+)
+public class SupervisingRouteControllerRestartTest {
+    @Autowired
+    private CamelContext context;
+
+    @Test
+    public void test() throws Exception {
+        Assert.assertNotNull(context.getRouteController());
+        Assert.assertTrue(context.getRouteController() instanceof SupervisingRouteController);
+
+        SupervisingRouteController controller = context.getRouteController().unwrap(SupervisingRouteController.class);
+
+        // Wait for the controller to start the routes
+        Thread.sleep(2500);
+
+        Assert.assertEquals(ServiceStatus.Started, context.getRouteStatus("foo"));
+        Assert.assertEquals(ServiceStatus.Started, context.getRouteStatus("bar"));
+        Assert.assertEquals(ServiceStatus.Started, context.getRouteStatus("jetty"));
+
+        // Wait a little
+        Thread.sleep(250);
+
+        controller.stopRoute("jetty");
+
+        Assert.assertNull(context.getRoute("jetty").getRouteContext().getRouteController());
+
+        // bind the port so starting the route jetty should fail
+        ServerSocket socket = new ServerSocket(TestConfiguration.PORT);
+
+        try {
+            controller.startRoute("jetty");
+        } catch (Exception e) {
+            assertThat(e).isInstanceOf(BindException.class);
+        }
+
+        // Wait for at lest one restart attempt.
+        Thread.sleep(2000);
+
+        try {
+            socket.close();
+        } catch (Exception e) {
+            fail("Failed to close server socket", e);
+        }
+
+        // Wait for wile to give time to the controller to start the route
+        Thread.sleep(1500);
+
+        Assert.assertEquals(ServiceStatus.Started, context.getRouteStatus("jetty"));
+        Assert.assertNotNull(context.getRoute("jetty").getRouteContext().getRouteController());
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public static class TestConfiguration {
+        private static final int PORT = AvailablePortFinder.getNextAvailable();
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() throws Exception {
+                    from("timer:foo?period=5s")
+                        .id("foo")
+                        .startupOrder(2)
+                        .to("mock:foo");
+                    from("timer:bar?period=5s")
+                        .id("bar")
+                        .startupOrder(1)
+                        .to("mock:bar");
+                    from("timer:unmanaged?period=5s")
+                        .id("timer-unmanaged")
+                        .to("mock:timer-unmanaged");
+                    from("timer:no-autostartup?period=5s")
+                        .id("timer-no-autostartup")
+                        .autoStartup(false)
+                        .to("mock:timer-no-autostartup");
+
+                    fromF("jetty:http://localhost:%d", PORT)
+                        .id("jetty")
+                        .to("mock:jetty");
+                }
+            };
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/a613a589/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerTest.java
----------------------------------------------------------------------
diff --git a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerTest.java b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerTest.java
new file mode 100644
index 0000000..b1a43f0
--- /dev/null
+++ b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerTest.java
@@ -0,0 +1,126 @@
+/**
+ * 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 org.apache.camel.CamelContext;
+import org.apache.camel.ServiceStatus;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.SupervisingRouteController;
+import org.apache.camel.test.AvailablePortFinder;
+import org.apache.camel.util.backoff.BackOff;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+    classes = {
+        CamelAutoConfiguration.class,
+        SupervisingRouteControllerAutoConfiguration.class,
+        SupervisingRouteControllerTest.TestConfiguration.class
+    },
+    properties = {
+        "camel.springboot.xml-routes = false",
+        "camel.springboot.main-run-controller = true",
+        "camel.supervising.controller.enabled = true",
+        "camel.supervising.controller.initial-delay = 2s",
+        "camel.supervising.controller.default-back-off.delay = 1s",
+        "camel.supervising.controller.default-back-off.max-attempts = 10",
+        "camel.supervising.controller.routes.bar.back-off.delay = 10s",
+        "camel.supervising.controller.routes.bar.back-off.max-attempts = 3",
+        "camel.supervising.controller.routes.timer-unmanaged.supervise = false"
+    }
+)
+public class SupervisingRouteControllerTest {
+    @Autowired
+    private CamelContext context;
+
+    @Test
+    public void test() throws Exception {
+        Assert.assertNotNull(context.getRouteController());
+        Assert.assertTrue(context.getRouteController() instanceof SupervisingRouteController);
+
+        SupervisingRouteController controller = context.getRouteController().unwrap(SupervisingRouteController.class);
+
+        Assert.assertEquals(3, controller.getControlledRoutes().size());
+        Assert.assertEquals(2, controller.getInitialDelay().getSeconds());
+
+        // Route foo
+        BackOff foo = controller.getBackOff("foo");
+        Assert.assertEquals(1, foo.getDelay().getSeconds());
+        Assert.assertEquals(Long.MAX_VALUE, foo.getMaxDelay().toMillis());
+        Assert.assertEquals(10L, foo.getMaxAttempts().longValue());
+
+        // Route bar
+        BackOff bar = controller.getBackOff("bar");
+        Assert.assertEquals(10, bar.getDelay().getSeconds());
+        Assert.assertEquals(Long.MAX_VALUE, bar.getMaxDelay().toMillis());
+        Assert.assertEquals(3L, bar.getMaxAttempts().longValue());
+
+        Assert.assertEquals(controller, context.getRoute("foo").getRouteContext().getRouteController());
+        Assert.assertEquals(controller, context.getRoute("bar").getRouteContext().getRouteController());
+        Assert.assertNull(context.getRoute("timer-unmanaged").getRouteContext().getRouteController());
+        Assert.assertNull(context.getRoute("timer-no-autostartup").getRouteContext().getRouteController());
+
+        Assert.assertEquals(ServiceStatus.Stopped, context.getRouteStatus("foo"));
+        Assert.assertEquals(ServiceStatus.Stopped, context.getRouteStatus("bar"));
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public static class TestConfiguration {
+        private static final int PORT = AvailablePortFinder.getNextAvailable();
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() throws Exception {
+                    from("timer:foo?period=5s")
+                        .id("foo")
+                        .startupOrder(2)
+                        .to("mock:foo");
+                    from("timer:bar?period=5s")
+                        .id("bar")
+                        .startupOrder(1)
+                        .to("mock:bar");
+                    from("timer:unmanaged?period=5s")
+                        .id("timer-unmanaged")
+                        .to("mock:timer-unmanaged");
+                    from("timer:no-autostartup?period=5s")
+                        .id("timer-no-autostartup")
+                        .autoStartup(false)
+                        .to("mock:timer-no-autostartup");
+
+                    fromF("jetty:http://localhost:%d", PORT)
+                        .id("jetty")
+                        .to("mock:jetty");
+                }
+            };
+        }
+    }
+}