Beanとの統合

Camelでは、Camelが提供するコンポーネントやDataFormatでは実現できない機能や業務処理を、Javaで実装し実行することができます。 ここでは、クラス(Bean)の呼び出し方や、パラメータのバインディングについて説明します。

Beanの呼び出し方

Processorインタフェース

Camelでユーザ独自の処理を実行するインタフェースとして、Processorインタフェースを提供しています。

processメソッドで、Exchangeを受け取り、何らかの処理を行います。例えば、ExchangeのBODYにある文字列を取得し、大文字にして変換して再度BODYに設定するには、以下の通りとなります。

public class BodyUpdateProcessor implements Processor {

   private static final Logger LOG = LoggerFactory.getLogger(BodyUpdateProcessor.class);
        
    @Override
    public void process(Exchange exchange) throws Exception {
        LOG.debug("BodyUpdateProcessor#process called.");
        if (exchange == null) {
            return;
        }    
        
        // BodyをStringで取得
        String body = exchange.getIn().getBody(String.class);
        if (body != null) {
            // Bodyの内容を大文字にして、再度Bodyに設定
            exchange.getIn().setBody(body.toUpperCase());
        }
    }
 }

このBodyUpdateProcessor を実行するには、<process><to>の2通りの方法があります。

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <!-- Processで実行 -->
        <route>
            <from uri="direct:process"/>
            <log message="(1) 実行前 body = ${body}" />
            <!-- 実行 -->
            <process ref="BodyUpdateProcessor"/>
            <log message="(2) 実行後 body = ${body}" />
            <to uri="mock:result"/>
        </route>

        <!-- Processで実行 -->
        <route>
            <from uri="direct:process_to"/>
            <log message="(1) 実行前 body = ${body}" />
            <!-- 実行 -->
            <to uri="BodyUpdateProcessor"/>
            <log message="(2) 実行後 body = ${body}" />
            <to uri="mock:result"/>
        </route>

    </camelContext>

    <!-- Processorとして定義 -->
    <bean id="BodyUpdateProcessor" class="com.buildria.camel.example.basis.bean.BodyUpdateProcessor" />

Bean

Processorインタフェースを実装すると、Camelの実装に依存することになりますが、既存のロジックを使用したい場合や、極力Camelに依存しないようにしたい場合があります。

例えば、下記の流通する文字列データを大文字に変更するBodyUpdaterクラスがあるとします。

public class BodyUpdater {

    private static final Logger LOG = LoggerFactory.getLogger(BodyUpdater.class);
    
    // 引数bodyには、ExchangeのBODYが渡されます。
    // 戻り値は、ExchangeのBODYに設定されます。
    public String toUpperCase(String body) {
        LOG.debug("BodyUpdater#toUpperCase called. body = [{}]", body);
        if (body == null) {
            return null;
        }
        return body.toUpperCase();
    }
    
}

このクラスを使用して、流通データを変換するには3つの方法があります。

いずれの方法でも、ExchangeのBODYがメソッドの引数bodyに渡されます。また、メソッドの戻り値がvoid型でない場合は、戻り値が実行後のBODYに設定されます。

<bean>による実行

<bean>タグのref属性でクラスのid(BodyUpdater)を指定し、method属性で実行するメソッド(toUpperCase)を指定します。

        <route>
            <from uri="direct:beanTag"/>
            <log message="(1) 実行前 body = ${body}" />
            <!-- 実行するBeanのid属性と実行するメソッドを指定する -->
            <bean ref="BodyUpdater" method="toUpperCase" />
            <log message="(2) 実行後 body = ${body}" />
            <to uri="mock:result"/>
        </route>
        :
    <!-- JavaBeansとして定義 -->
    <bean id="BodyUpdater" class="com.buildria.camel.example.basis.bean.BodyUpdater" />

Beanコンポーネントによる実行

<to>タグを使用し、uri属性に実行するBeanのid属性とメソッド名を指定します。

        <route>
            <from uri="direct:beanComponent"/>
            <log message="(1) 実行前 body = ${body}" />
            <!-- Beanコンポーネントで実行 -->
            <to uri="bean:BodyUpdater?method=toUpperCase" />
            <log message="(2) 実行後 body = ${body}" />
            <to uri="mock:result"/>
        </route>

エンドポイントとして実行

上記と似ていますが、Beanをエンドポイントとして実行する方法もあります。この場合、メソッド名は指定できません。Camelの独自の選択ルールにより、実行するメソッド名が決定します。

        <route>
            <from uri="direct:beanEndpoint"/>
            <log message="(1) 実行前 body = ${body}" />
            <!-- エンドポイントとして実行 -->
            <to uri="BodyUpdater" />
            <log message="(2) 実行後 body = ${body}" />
            <to uri="mock:result"/>
        </route>

パラメータのバインディング

Camelは、ユーザ実装クラス(Bean)のメソッドを呼び出す際に、メソッドのパラメータに値をバインディングします。

上記の例で説明したとおり、基本的にメソッドの一番最初のパラメータにExchangeにIn MessageのBODYをバインディングします。その際に、BODYの値をパラメータの型に自動的に変換します。変換できない場合はエラーが発生します。

Camelの特定クラス

パラメータの型が、CamelやJavaが提供する次のクラスである場合は、そのクラスに応じた値をバインドします。

よく使われるExchangeをバインドする例を以下に示します。

    /**
     * <p>{@link Exchange}をバインドします。</p>
     *
     * @param exchange Exchange
     */
    public void bindExchange(Exchange exchange) {
        if (exchange == null) {
            return;
        }
        log.debug("Exchange: {}", exchange);
        
        // ヘッダを取得
        Map<String, Object> headers = exchange.getIn().getHeaders();
        for (Map.Entry<String, Object> header : headers.entrySet()) {
            log.debug("ヘッダ  key: [{}] 値: [{}]", new Object[] { header.getKey(), header.getValue()});
        }
        
        // BODYを取得
        String body = exchange.getIn().getBody(String.class);
        log.debug("BODY [{}]", body);
    }

アノテーション

Exchangeを使用することで、プロパティ、ヘッダ、BODYを取得することができますが、個別にパラメータにバインドしたい場合は、次のアノテーションをメソッドのパラメータに設定します。

アノテーション バインドする値
@Body In MessageのBODY @Body Object body
@ExchangeException Exchnageに設定された例外 @ExchangeException Exception exception
@Header In Messageの特定のヘッダの値 @Header(“type”) String type
@Headers In Messageのヘッダの値(Map) @Headers Map<String, Object> headers
@OutHeaders Out Messageのヘッダの値(Map) @OutHeaders Map<String, Object> headers
@Property Exchangeの特定のプロパティの値 @Property(“type”) String type
@PropertiesExchangeのプロパティの値(Map)@Properties Map<String, Object> properties

BODYは、一番最初のパラメータに通常自動的にバインドされますが、2番目以降のパラメータにBODYをバインドしたい場合は@Bodyを使用します。

ヘッダtypeの値とBODYの値をバインドする例を以下に示します。

    /**
     * <p>アノテーションでヘッダの値とBODYをバインドします。</p>
     * 
     * @param type ヘッダのtypeの値
     * @param body BODY
     */
    public void bindHeaderValues(@Header("type") String type, @Body String body) {
        log.debug("ヘッダ(type): {}", type);
        log.debug("BODY: {}", body);
    }

上記以外にも、@Xpath等の言語特有のアノテーションによるバインディングもサポートしています。詳しくは、Annotation Based Expression Languageを参照してください。

参考情報