Routing Slip

概要

Camelでは、入力のメッセージに対して静的にルーチングルールを設計します。しかし、メッセージによって実行する処理が変わることがあります。

Routing Slipでは、実行するエンドポイントを、前処理でヘッダ等に動的に設定し、その値を参照して処理を実行します。


Routing Slip

基本

ヘッダroutingSlipHeaderに設定されたエンドポイントを実行する場合を以下に示します。 <routingSLip>の子要素<simple>に、ヘッダroutingSlipHeaderを参照するよう設定しています。

また、UriDelimiter属性に複数のエンドポイントを設定する際のデリミタを、存在しないエンドポイントは無視するようにignoreinvalidEndpoints属性にtrue(デフォルトはfalse)を設定しています。

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <route>
            <from uri="direct:roundslip"/>
            <log message="(1) before: ${header.routingSlipHeader}" />
            <!-- デリミタは"#"、存在しないエンドポイントは無視 -->
            <routingSlip uriDelimiter="#" ignoreInvalidEndpoints="true" >
                <simple>${header.routingSlipHeader}</simple>
            </routingSlip>
            <log message="(3) end" />
            <to uri="mock:result" />
        </route>
        
        <route>
            <from uri="direct:route_a" />
            <log message="(2-a) route_a: ${body}" />
        </route>
        <route>
            <from uri="direct:route_b" />
            <log message="(2-b) route_b: ${body}" />
        </route>
        <route>
            <from uri="direct:route_c" />
            <log message="(2-c) route_c: ${body}" />
        </route>                          
    </camelContext>
  

ヘッダroutingSlipHeaderにエンドポイントdirect:route_aを1つ設定してみると、

    public void testRoute() throws Exception {  
        template.sendBodyAndHeader("direct:roundslip", "Data", "routingSlipHeader", "direct:route_a");
    }

direct:route_aが動的に実行されたことがわかります。

[main] (1) before: direct:route_a
[main] (2-a) route_a: Data
[main] (3) end

また、複数設定してみると、

    public void testRoutes() throws Exception {  
        template.sendBodyAndHeader("direct:roundslip", "Data", 
            "routingSlipHeader", "direct:route_a#direct:route_b#direct:route_c");
    }

設定した順番に動的に実行されます。

[main] (1) before: direct:route_a#direct:route_b#direct:route_c
[main] (2-a) route_a: Data
[main] (2-b) route_b: Data
[main] (2-c) route_c: Data
[main] (3) end

応用

上記の例の場合、定義しているエンドポイントを動的に設定していました。 もし、各都道府県にあるセンターから電文を受け取り、都道府県ごとのディレクトリに格納する場合を考えて見ます。

ヘッダprefに都道府県名を設定して、メッセージを受け取るとします。

    public void testRoute() throws Exception {  
        List<String> prefs = new ArrayList<String>() {
            {
                add("tokyo");
                add("kanagawa");
                add("saitama");
            }
        };
        // ヘッダ"pref"に都道府県を設定        
        for (String pref : prefs) {
            template.sendBodyAndHeader("direct:start", pref + "のデータ", "pref", pref);
        }
    }

この場合、ヘッダprefごとに判断してファイルを保存することもできますが、47都道府県分設定する必要があります。

    <route>
        <from uri="xxxx">
        <choice>
            <when>
               <simple>${header.pref} == 'tokyo'</simple>
               <!-- ファイルコンポーネントで東京のディレクトリに保存 -->
               <to uri="file:target/work/tokyo" />
           </when>
            <when>
               <simple>${header.pref} == 'kanagawa'</simple>
               <!-- ファイルコンポーネントで神奈川のディレクトリに保存 -->
               <to uri="file:target/work/kanagawa" />
           </when>
                : 繰り返し    
         </choice>
   </route>                     

これは面倒なので、ヘッダprefの値から動的に実行するファイルコンポーネントのURIを生成するBeanを作成します。 このMyFileURICreator#createは、Exchangeを引数として受け取り、生成したファイルコンポーネントのURIを戻り値として返します。

public class MyFileURICreator {

    public String create(Exchange exchange) throws Exception {
        // ヘッダ"pref"の値を取得
        String pref = exchange.getIn().getHeader("pref", String.class);
        // FileプロデューサのURIを生成
       return "file:target/work/" + pref;
    }
}

そして、<method>を使用し戻り値をエンドポイントとして実行するように、Spring XMLを変更します。

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <route>
            <from uri="direct:start"/>
            <log message="(1) pref ... ${header.pref}" />
            <!-- デリミタは"#"、存在しないエンドポイントは無視 -->
            <routingSlip uriDelimiter="#" ignoreInvalidEndpoints="true" >
                <!-- ファイルの保存先を決定する -->
                <!-- MyFileURICreator#createの戻り値から動的に処理を実行 -->
                <method ref="MyFileURICreator" method="create" />
            </routingSlip>
            <!-- Exchange.SLIP_ENDPOINTに最後に実行したエンドポイントが設定される -->
            <log message="(2) end. ${property.CamelSlipEndpoint}" />
            <to uri="mock:result" />
        </route>
    </camelContext>    
    <!-- FileプロデューサのURIを生成するBean -->
    <bean id="MyFileURICreator" 
          class="com.buildria.camel.example.eip.routingslip.MyFileURICreator" />

FTPコンポーネントで接続先が動的に変わる場合など便利ですが、多用すると、Spring XMLを見ただけでは処理がわからなくなるので、限定的に使用するほうがメンテナンスしやすいでしょう。

関連項目