How can I stop a route from a route

The CamelContext provides API for managing routes at runtime. It has a stopRoute(id) and startRoute(id) methods.

Stopping a route during routing an existing message is a bit tricky. The reason for that is Camel will Graceful Shutdown the route you are stopping. And if you do that while a message is being routed the Graceful Shutdown will try to wait until that message has been processed.

The best practice for stopping a route from a route, is to either:

  • signal to another thread to stop the route

  • spin off a new thread to stop the route

Using another thread to stop the route is also what is normally used when stopping Camel itself, or for example when an application in a server is stopped etc. Its too tricky and hard to stop a route using the same thread that currently is processing a message from the route. This is not advised to do, and can cause unforeseen side effects.

Using a latch to stop Camel from a route

In this example we use a CountdownLatch to signal when Camel should stop, triggered from a route.

// use a latch as signal when to stop Camel
private final CountDownLatch latch = new CountDownLatch(1);

public void testStopCamelFromRoute() throws Exception {
    // create camel, add routes, and start camel
    CamelContext context = new DefaultCamelContext();
    context.addRoutes(createMyRoutes());
    context.start();

    // setup mock expectations for unit test
    MockEndpoint start = context.getEndpoint("mock:start", MockEndpoint.class);
    start.expectedMessageCount(1);
    MockEndpoint done = context.getEndpoint("mock:done", MockEndpoint.class);
    done.expectedMessageCount(1);

    // send a message to the route
    ProducerTemplate template = context.createProducerTemplate();
    template.sendBody("direct:start", "Hello Camel");

    // wait for the latch (use 1 minute as fail safe, due unit test)
    assertTrue(latch.await(1, TimeUnit.MINUTES));

    // stop camel
    context.stop();

    // unit test assertions
    start.assertIsSatisfied();
    done.assertIsSatisfied();
}

And in the route we call the latch as shown:

public RouteBuilder createMyRoutes() throws Exception {
    return new RouteBuilder() {
        @Override
        public void configure() throws Exception {
            from("direct:start").routeId("myRoute")
                .to("mock:start")
                .process(new Processor() {
                    @Override
                    public void process(Exchange exchange) throws Exception {
                        // stop Camel by signalling to the latch
                        latch.countDown();
                    }
                }).to("mock:done");
        }
    };
}

Using a thread to stop a route from a route

In this example we use a separate Thread to stop the route, triggered from the route itself.

public void testStopRouteFromRoute() throws Exception {
    // create camel, add routes, and start camel
    CamelContext context = new DefaultCamelContext();
    context.addRoutes(createMyRoutes());
    context.start();

    assertTrue("Route myRoute should be started", context.getRouteStatus("myRoute").isStarted());
    assertTrue("Route bar should be started", context.getRouteStatus("bar").isStarted());

    // setup mock expectations for unit test
    MockEndpoint start = context.getEndpoint("mock:start", MockEndpoint.class);
    start.expectedMessageCount(1);
    MockEndpoint done = context.getEndpoint("mock:done", MockEndpoint.class);
    done.expectedMessageCount(1);

    // send a message to the route
    ProducerTemplate template = context.createProducerTemplate();
    template.sendBody("direct:start", "Hello Camel");

    // just wait a bit for the thread to stop the route
    Thread.sleep(1000);

    // the route should now be stopped
    assertTrue("Route myRoute should be stopped", context.getRouteStatus("myRoute").isStopped());
    assertTrue("Route bar should be started", context.getRouteStatus("bar").isStarted());

    // stop camel
    context.stop();

    // unit test assertions
    start.assertIsSatisfied();
    done.assertIsSatisfied();
}

And in the route we create the thread and call the stopRoute method as shown:

public RouteBuilder createMyRoutes() throws Exception {
    return new RouteBuilder() {
        @Override
        public void configure() throws Exception {
            from("direct:start").routeId("myRoute")
                .to("mock:start")
                .process(new Processor() {
                    Thread stop;

                    @Override
                    public void process(final Exchange exchange) throws Exception {
                        // stop this route using a thread that will stop
                        // this route gracefully while we are still running
                        if (stop == null) {
                            stop = new Thread() {
                                @Override
                                public void run() {
                                    try {
                                        exchange.getContext().getRouteController().stopRoute("myRoute");
                                    } catch (Exception e) {
                                        // ignore
                                    }
                                }
                            };
                        }

                        // start the thread that stops this route
                        stop.start();
                    }
                }).to("mock:done");

            from("direct:bar").routeId("bar")
                .to("mock:bar");
        }
    };
}

Alternative solutions

Camel provides another feature for managing routes at runtime which is RoutePolicy.

And CamelContext also provides API for suspend/resume of routes, and shutdown as well.

  • suspend/resume is faster than stop/start. For example a HTTP server will still run but deny any incoming requests. Whereas if it was stopped the HTTP listener would have been stopped.

  • shutdown means the route is being removed from CamelContext and cannot be started again. Its also removed from JMX. A route must have been stopped prior to be shutdown.

See more details about the Lifecycle.

You can also use the ControlBus component to let it stop/start routes.