3

I have couple of routes (route 1 and route 2) in my Spring Boot application. I have been researching how to gracefully shutdown the application after processing both the routes. I have referred the documentation (https://camel.apache.org/manual/latest/graceful-shutdown.html) but couldn't successfully achieve what I needed. Maybe my understanding is wrong.

Below are my two routes

Route 1

 from("timer://runOnce?repeatCount=1")
     .to("{{sql.selectAll}}")
......... SOME PROCESSING
     .to("direct:checkStatus")

Route 2

from("direct:checkStatus")
     .delay(5000)
     .loopDoWhile(CONDITION)
          .process(DO_SOMETHING)
     .end()
     .to("jpa:com.pqr.MyClass)
     .stop();

I have tried all these options

1. Automatic shutdown after 60 seconds

camel.springboot.duration-max-seconds = 60

It does GRACEFULLY shutdown the 2 routes but then WARNs about FORCEFUL shutdown ExecutorsService and also it doesn't stop the main thread to stop the application.

2020-03-01 18:28:25.507  WARN 30279 --- [otTerminateTask] o.a.c.i.e.BaseExecutorServiceManager     : Forcing shutdown of ExecutorService: org.apache.camel.util.concurrent.SizedScheduledExecutorService@17fbfb02[CamelSpringBootTerminateTask] due first await termination elapsed.
2020-03-01 18:28:25.507  WARN 30279 --- [otTerminateTask] o.a.c.i.e.BaseExecutorServiceManager     : Forcing shutdown of ExecutorService: org.apache.camel.util.concurrent.SizedScheduledExecutorService@17fbfb02[CamelSpringBootTerminateTask] due interrupted.
2020-03-01 18:28:25.508  INFO 30279 --- [otTerminateTask] o.a.c.i.e.BaseExecutorServiceManager     : Shutdown of ExecutorService: org.apache.camel.util.concurrent.SizedScheduledExecutorService@17fbfb02[CamelSpringBootTerminateTask] is shutdown: true and terminated: false took: 10.004 seconds.
2020-03-01 18:28:25.508  WARN 30279 --- [otTerminateTask] o.a.c.i.e.BaseExecutorServiceManager     : Forced shutdown of 1 ExecutorService's which has not been shutdown properly (acting as fail-safe)
2020-03-01 18:28:25.508  WARN 30279 --- [otTerminateTask] o.a.c.i.e.BaseExecutorServiceManager     :   forced -> org.apache.camel.util.concurrent.SizedScheduledExecutorService@17fbfb02[CamelSpringBootTerminateTask]

2. Initiate shutdown from the Route2

    from("direct:checkStatus")
            .delay(5000)
            .loopDoWhile(CONDITION)
                 .process(DO_SOMETHING)
            .end()
            .to("jpa:com.pqr.MyClass)
            .process(exchange -> {
                exchange.getContext().getRouteController().stopRoute("route1");
                exchange.getContext().getRouteController().stopRoute("route2");
                System.out.println("Route1 -->"+exchange.getContext().getRouteController().getRouteStatus("route1"));
                System.out.println("Route2 -->"+exchange.getContext().getRouteController().getRouteStatus("route2"));
                exchange.getContext().shutdown();
            });

"route1" is gracefully stopped but "route2" fails to be gracefully stopped with below message and waits for default timeout (300s).

2020-03-01 18:35:29.113  INFO 30504 --- [read #4 - Delay] o.a.c.i.engine.DefaultShutdownStrategy   : Starting to graceful shutdown 1 routes (timeout 300 seconds)
2020-03-01 18:35:29.116  INFO 30504 --- [ - ShutdownTask] o.a.c.i.engine.DefaultShutdownStrategy   : Route: route1 shutdown complete, was consuming from: timer://runOnce?repeatCount=1
2020-03-01 18:35:29.116  INFO 30504 --- [read #4 - Delay] o.a.c.i.engine.DefaultShutdownStrategy   : Graceful shutdown of 1 routes completed in 0 seconds
2020-03-01 18:35:29.117  INFO 30504 --- [read #4 - Delay] o.a.c.s.boot.SpringBootCamelContext      : Route: route1 is stopped, was consuming from: timer://runOnce?repeatCount=1
2020-03-01 18:35:29.117  INFO 30504 --- [read #4 - Delay] o.a.c.i.engine.DefaultShutdownStrategy   : Starting to graceful shutdown 1 routes (timeout 300 seconds)
2020-03-01 18:35:29.118  INFO 30504 --- [ - ShutdownTask] o.a.c.i.engine.DefaultShutdownStrategy   : Waiting as there are still 1 inflight and pending exchanges to complete, timeout in 300 seconds. Inflights per route: [route2 = 1]

It looks like there is a pending exchange message to be consumed. Do I need to manually clear/consume the exchange message in order to clear and facilitate a graceful shutdown?

Either option doesn't stop the main application. Do I have to write a custom Shutdown strategy instead of DefaultShutdownStrategy to achieve this? Can someone kindly point to an example to shut down the Spring Boot application after completion of the routes? Thanks in advance!!!

user1637487
  • 241
  • 1
  • 9
  • 17
  • I think you can get SpringApplicationContext and call exit method on application on passing context as show here: https://www.baeldung.com/spring-boot-shutdown#exit, do this once process is completed – dkb Mar 02 '20 at 11:06

4 Answers4

2

Did you try to use exchange.getContext().stop() to stop main application?

To force stop route without waiting for default timeout you can use exchange.getContext().stopRoute(routeId, 1L, TimeUnit.SECONDS); or set your timeout in seconds context.getShutdownStrategy().setTimeout(30);

Lukyanov Mikhail
  • 500
  • 7
  • 17
  • 1
    Hi, Yes I did try using exchange.getContext().stop(); as a last step in my route2. It still says "Waiting as there are still 1 inflight and pending exchanges to complete". Do I need to remove the exchange message before initiating Stop sequence? – user1637487 Mar 03 '20 at 17:57
  • Hi. You can try to clean inflight repository https://access.redhat.com/webassets/avalon/d/red-hat-jboss-fuse/6.3/api-reference/camel/org/apache/camel/spi/InflightRepository.html before stop context, but pending exchanges may stay. The most reliable option is to force shut down all routes or reduce the shutdown timeout, which I wrote about in the answer. – Lukyanov Mikhail Mar 03 '20 at 19:05
0

You have to stop the currently running route from a new thread. The onCompletion() DSL is to make sure every message has been processed.

The attached code is in Kotlin, but it should be easy to transfer it to Java:

 fromF(route).id(routeId)
            .process(someProcessor)
            .to("jdbc:dataSource")
            .onCompletion()
            .choice().`when`(exchangeProperty("CamelBatchComplete"))
            .process(object : Processor {
                override fun process(exchange: Exchange) {
                    Thread {
                        try {
                            exchange.context.routeController.stopRoute(routeId)
                            exchange.context.stop()
                        } catch (e: Exception) {
                            throw RuntimeException(e)
                        }
                    }.start()

                }
            }
            )
            // must use end to denote the end of the onCompletion route
            .end()

If you want to stop the entire application, you can use this class and add a call of shutdownManager.initiateShutdown() after the exchange.context.stop().

@Component
class ShutdownManager {

    companion object {
        val logger = LoggerFactory.getLogger(ShutdownManager::class.java)
    }

    @Autowired
    private val appContext: ApplicationContext? = null


    fun initiateShutdown(returnCode: Int) {
        logger.info("Shutting down with a Shutdown manager")
        SpringApplication.exit(appContext, ExitCodeGenerator { returnCode })
        System.exit(returnCode)
    }
}
isixtova
  • 29
  • 4
0

In Spring Boot you can add the following application property to shut down automatically if no messages are being processed...

camel.springboot.duration-max-idle-seconds=15
robleney
  • 56
  • 3
0

I got trouble like this so i just added global "onCompletion" and shutdown context like this:

    
    @Override
    public void configure() {
        onCompletion()
            .process(exchange -> exchange.getContext().shutdown());

        from("timer://foo?repeatCount=2").routeId("foo")
            ...someLogic...
            .to("bean:greetingsBean");
    }
VlIsHere
  • 11
  • 2