Apache Camelのrouteの起動・停止/起動順を制御する

# はじめに

Camelのアプリケーションが起動すると、ルートもデフォルトで自動起動します。Camelのアプリケーションが停止するときも、ルートが自動で停止するようになっています。
このときのルートの起動・停止順は不定であり、厳密に起動順を制御しなければならないアプリケーションのために、Camelでは起動順を制御することができるようになっています。
例えば、ルートA→ルートBでデータが流れるのであれば、ルートB、ルートAの順で起動させなければなりません。そうしないと、ルートAで処理したデータが、ルートBに流れないか、もしくはエラーになるかもしれません（試したことはありませんが）。そもそも、アプリケーションの起動が終わるまではデータを流さなければいいので、起動順はあまり気にしないことが多いと思います。
ただし、停止順はデータロストにつながる可能性があるため、順序を気にすることが多いかと思います。
例えば、ルートA→ルートBでデータが流れるのであれば、ルートBが先に停止してしまうと、ルートAで処理中のデータがあれば、それは処理が完了しないことになります。



# routeの自動起動をオフにする

routeはデフォルトで自動起動がオンになっています。
これをオフにして自動起動しないようにするためには、「autoStartup="false"」をrouteに追加します。

```camel-context.xml
<route id="mainRoute" autoStartup="false">
```

# routeの起動順を制御する

以下、order1route、order2route、order3routeという3つのrouteを定義しています。
この場合、routeの起動順は不定になります。ただし、実際に数回動かしてみたところ、上から順番に起動するようです。

```camel-context.xml
		<route id="order3route">
			<from uri="direct:order3route" />
			<to uri="mock:result" />
		</route>

		<route id="order2route">
			<from uri="direct:order2route" />
			<to uri="mock:result" />
		</route>

		<route id="order1route">
			<from uri="direct:order1route" />
			<to uri="mock:result" />
		</route>
```

上のrouteを動かしたときのログは以下のようになります。
「Route: order[X]route started」のログで、order3route、order2route、order1routeの順番で起動していることが分かります。

```
[2018-12-04 07:43:49.682], [INFO ], o.a.c.s.SpringCamelContext, main, org.apache.camel.spring.SpringCamelContext, Route: order3route started and consuming from: direct://order3route
[2018-12-04 07:43:49.682], [INFO ], o.a.c.s.SpringCamelContext, main, org.apache.camel.spring.SpringCamelContext, Route: order2route started and consuming from: direct://order2route
[2018-12-04 07:43:49.683], [INFO ], o.a.c.s.SpringCamelContext, main, org.apache.camel.spring.SpringCamelContext, Route: order1route started and consuming from: direct://order1route
```

次に起動順を指定してアプリケーションを実行します。
routeに「startupOrder="[起動順序]"」を追加することで起動順を制御できます。
この起動順序は、数値が小さいrouteから順に起動します。なお、起動順を設定していないrouteは起動順が1000以上の値が設定されます（らしいです）。

起動順を指定しない場合、routeが定義された上から順に実行されていました。そのため、今度は起動順を指定して、下からrouteが実行されるか試してみます。

```
		<route id="order3route" startupOrder="3">
			<from uri="direct:order3route" />
			<to uri="mock:result" />
		</route>

		<route id="order2route" startupOrder="2">
			<from uri="direct:order2route" />
			<to uri="mock:result" />
		</route>

		<route id="order1route" startupOrder="1">
			<from uri="direct:order1route" />
			<to uri="mock:result" />
		</route>
```

実行結果は以下のとおりで、order1route、order2route、order3routeという、起動順の値が小さいrouteから起動していることが確認できます。

```
[2018-12-04 07:43:10.839], [INFO ], o.a.c.c.s.SedaEndpoint, main, org.apache.camel.component.seda.SedaEndpoint, Endpoint seda://seda_test is using shared queue: seda://seda_test with size: 1000
[2018-12-04 07:43:10.855], [INFO ], o.a.c.s.SpringCamelContext, main, org.apache.camel.spring.SpringCamelContext, Route: order1route started and consuming from: direct://order1route
[2018-12-04 07:43:10.857], [INFO ], o.a.c.s.SpringCamelContext, main, org.apache.camel.spring.SpringCamelContext, Route: order2route started and consuming from: direct://order2route
[2018-12-04 07:43:10.858], [INFO ], o.a.c.s.SpringCamelContext, main, org.apache.camel.spring.SpringCamelContext, Route: order3route started and consuming from: direct://order3route
```

# 自動停止について

アプリケーションをシャットダウンしたときは、routeは起動順と逆に停止します。
order1route、order2route、order3routeの順番で起動させていた場合は、order3route、order2route、order1routeの順番に停止することになります。

routeが停止する際に処理中のデータ（Exchange）があった場合は、処理中のデータが無くなるまで停止処理を待機します（Graceful Shutdownと呼びます）。ただし、デフォルトで300秒経過しても処理中のデータがあって停止が完了しない場合は、強制的に停止されます。

# プログラム中でrouteを起動・停止させる

routeを起動・停止させるためには、CamelContextのstartRouteメソッドとstopRouteメソッドを使用します。引数にはrouteIdを指定します。

```java
context.startRoute("noAutostart");
context.stopRoute("noAutostart");
```

routeの起動・停止を確認するためのプロセッサーです。
ExchangeのHeaderにstartFlg=trueであればrouteIdがnoAutostartのrouteを起動させ、startFlg=falseであれば停止させています。

```java
package sample;

import java.util.Map;

import javax.annotation.Resource;

import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class TestProcessor implements Processor {
	Logger logger = LoggerFactory.getLogger(TestProcessor.class);

	@Resource
	private CamelContext context;

    @Override
    public void process(Exchange exchange) throws Exception {
    	Map<String, Object> headers = exchange.getIn().getHeaders();
    	boolean startFlg = Boolean.parseBoolean((String)headers.get("startFlg"));
    	if (startFlg) {
    		logger.info("route start");
    		context.startRoute("noAutostart");
    	} else {
    		logger.info("route stop");
    		context.stopRoute("noAutostart");
    	}
    }
}
```

以下のようにrouteを定義しています。
・noAutostartのrouteは自動起動をオフにする。
・アプリケーション起動から5秒後（delay要素部分）にnoAutostartのrouteを起動させる。
・noAutostartのrouteは、起動すると1秒おきに「1 seconds timer」をログに出力する。
・アプリケーション起動から5秒後（delay要素部分）にnoAutostartのrouteを停止させる。

```camel-context.xml
	<context:component-scan base-package="sample" />
	<bean id="testProcessor" class="sample.TestProcessor" />

	<camelContext
		xmlns="http://camel.apache.org/schema/spring">

		<route id="noAutostart" autoStartup="false">
			<from uri="timer://run?delay=1000&amp;period=1000" />
			<log message="1 seconds timer" loggingLevel="INFO" />
		</route>

		<route id="main">
			<from uri="timer://runOnce?repeatCount=1&amp;delay=2000" />
			<log message="main route start" />
			<setHeader headerName="startFlg">
				<simple>true</simple>
			</setHeader>
			<delay asyncDelayed="false">
       			<constant>5000</constant>
   			</delay>
			<process ref="testProcessor" />
			<delay asyncDelayed="false">
       			<constant>5000</constant>
   			</delay>
   			<setHeader headerName="startFlg">
				<simple>false</simple>
			</setHeader>
			<process ref="testProcessor" />
			<to uri="mock:result" />
		</route>
	</camelContext>
```

実行結果のログは以下のとおり。

```
[2018-12-05 06:04:42.080], [INFO ], main, Camel (camel-1) thread #1 - timer://runOnce, main, main route start
[2018-12-05 06:04:47.087], [INFO ], s.TestProcessor, Camel (camel-1) thread #1 - timer://runOnce, sample.TestProcessor, route start
[2018-12-05 06:04:47.091], [INFO ], o.a.c.s.SpringCamelContext, Camel (camel-1) thread #1 - timer://runOnce, org.apache.camel.spring.SpringCamelContext, Route: noAutostart started and consuming from: timer://run?delay=1000&period=1000
[2018-12-05 06:04:48.091], [INFO ], noAutostart, Camel (camel-1) thread #2 - timer://run, noAutostart, 1 seconds timer
[2018-12-05 06:04:49.092], [INFO ], noAutostart, Camel (camel-1) thread #2 - timer://run, noAutostart, 1 seconds timer
[2018-12-05 06:04:50.092], [INFO ], noAutostart, Camel (camel-1) thread #2 - timer://run, noAutostart, 1 seconds timer
[2018-12-05 06:04:51.093], [INFO ], noAutostart, Camel (camel-1) thread #2 - timer://run, noAutostart, 1 seconds timer
[2018-12-05 06:04:52.092], [INFO ], s.TestProcessor, Camel (camel-1) thread #1 - timer://runOnce, sample.TestProcessor, route stop
[2018-12-05 06:04:52.094], [INFO ], noAutostart, Camel (camel-1) thread #2 - timer://run, noAutostart, 1 seconds timer
[2018-12-05 06:04:52.094], [INFO ], o.a.c.i.DefaultShutdownStrategy, Camel (camel-1) thread #1 - timer://runOnce, org.apache.camel.impl.DefaultShutdownStrategy, Starting to graceful shutdown 1 routes (timeout 300 seconds)
[2018-12-05 06:04:52.105], [INFO ], o.a.c.i.DefaultShutdownStrategy, Camel (camel-1) thread #3 - ShutdownTask, org.apache.camel.impl.DefaultShutdownStrategy, Route: noAutostart shutdown complete, was consuming from: timer://run?delay=1000&period=1000
[2018-12-05 06:04:52.106], [INFO ], o.a.c.i.DefaultShutdownStrategy, Camel (camel-1) thread #1 - timer://runOnce, org.apache.camel.impl.DefaultShutdownStrategy, Graceful shutdown of 1 routes completed in 0 seconds
[2018-12-05 06:04:52.108], [INFO ], o.a.c.s.SpringCamelContext, Camel (camel-1) thread #1 - timer://runOnce, org.apache.camel.spring.SpringCamelContext, Route: noAutostart is stopped, was consuming from: timer://run?delay=1000&period=1000
```

# 最後に

Apache Camelのrouteの起動・停止/起動順を制御する方法について記載しましたが、記載した内容は一部のみです（私にはこれだけで十分だったので他は調べていない）。
今回は試していませんが、アプリケーションのシャットダウン時の動作をカスタマイズすることもできるようです。


# 参考

How can I stop a route from a route
http://camel.apache.org/how-can-i-stop-a-route-from-a-route.html
Graceful Shutdown
http://camel.apache.org/graceful-shutdown.html
BasicPrinciples-StartupShutdown
https://access.redhat.com/documentation/en-us/red_hat_fuse/7.1/html/apache_camel_development_guide/basicprinciples#BasicPrinciples-StartupShutdown