36.3. アノテーションを使用した型コンバーターの実装

概要

型変換メカニズムは、新しい worker 型コンバーターを追加することで簡単にカスタマイズできます。ここでは、worker 型コンバーターの実装方法と、アノテーション型コンバーターローダーによって自動的にロードされるように Apache Camel と統合する方法を説明します。

型コンバーターの実装方法

カスタム型コンバーターを実装するには、以下の手順にしたがいます。

アノテーションが付けられたコンバータークラスの実装

@Converter アノテーションを使用してカスタム型コンバータークラスを実装できます。クラス自体にアノテーションを付け、型変換を実行する各 static メソッドにもアノテーションを付ける必要があります。各コンバーターメソッドは from 型を定義する引数を取り、オプションで 2 番目の Exchange 引数を取り、 to 型を定義する void でない戻り値を持ちます。型コンバーターローダーは Java リフレクションを使用してアノテーション付きのメソッドを見つけ、それらを型コンバーターメカニズムに統合します。例36.3「アノテーションが付けられたコンバータークラスの例」 は、java.io.File から java.io.InputStream へ変換するためのコンバーターメソッドと、byte[] から String へ変換するための別のコンバーターメソッド (引数 Exchange を含む) を定義するアノテーション付きコンバータークラスの例を示しています。

例36.3 アノテーションが付けられたコンバータークラスの例

package com.YourDomain.YourPackageName;

import org.apache.camel.Converter;

import java.io.*;

@Converter
public class IOConverter {
    private IOConverter() {
    }

    @Converter
    public static InputStream toInputStream(File file) throws FileNotFoundException {
        return new BufferedInputStream(new FileInputStream(file));
    }

    @Converter
    public static String toString(byte[] data, Exchange exchange) {
        if (exchange != null) {
            String charsetName = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
            if (charsetName != null) {
                try {
                    return new String(data, charsetName);
                } catch (UnsupportedEncodingException e) {
                    LOG.warn("Can't convert the byte to String with the charset " + charsetName, e);
                }
            }
        }
        return new String(data);
    }
}

toInputStream() メソッドは File 型から InputStream 型への変換を実行し、toString() メソッドは byte[] 型から String 型への変換を実行します。

注記

メソッド名は重要ではなく、自由に定義できます。重要なのは、引数の型、戻り値の型、および @Converter アノテーションです。

TypeConverter ファイルの作成

カスタムコンバーターの検出メカニズム ( アノテーション型コンバーターローダー で実装されている) を有効にするには、以下の場所に TypeConverter ファイルを作成します。

META-INF/services/org/apache/camel/TypeConverter

この TypeConverter ファイルには、型コンバータークラスの FQN (完全修飾名) がコンマ区切りで列挙されたリストが含まれている必要があります。たとえば、型コンバーターローダーが YourPackageName.YourClassName パッケージからアノテーション付きコンバータークラスを検索する場合は、TypeConverter ファイルの内容は以下のようになります。

com.PackageName.FooClass

検索メカニズムを有効にする別の方法は、TypeConverter ファイルにパッケージ名のみを追加することです。たとえば、TypeConverter ファイルの内容は以下のようになります。

com.PackageName

これにより、パッケージスキャナーが @Converter タグのパッケージをスキャンします。FQN メソッドの使用は高速であり、推奨される方法です。

型コンバーターのパッケージ化

型コンバーターは、カスタム型コンバーターのコンパイルされたクラスと META-INF ディレクトリーを含む JAR ファイルとしてパッケージ化されます。この JAR ファイルをクラスパスに置き、Apache Camel アプリケーションで利用できるようにします。

フォールバックコンバーターメソッド

@Converter アノテーションを使用して通常のコンバーターメソッドを定義する他に、@FallbackConverter アノテーションを使用してフォールバックコンバーターメソッドを定義することもできます。フォールバックコンバーターメソッドは、controller 型コンバーターが型レジストリーで通常のコンバーターメソッドの検索に失敗した場合にのみ試行されます。

通常のコンバーターメソッドとフォールバックコンバーターメソッドの基本的な違いは、通常のコンバーターメソッドが特定の型 (例: byte[] から String など) のペア間で変換を実行するのに対し、フォールバックコンバーターは、潜在的に任意 の型のペア間で変換できるところです。どの変換を実行できるかは、フォールバックコンバーターメソッド本体の実装ロジック次第です。実行時に通常のコンバーターで変換できない場合、controller 型コンバーターは変換できるコンバーターを見つけるまで、利用可能なすべてのフォールバックコンバーターを繰り返し実行します。

フォールバックコンバーターのメソッドの署名には、以下のいずれかの形式を使用できます。

// 1. Non-generic form of signature
@FallbackConverter
public static Object MethodName(
    Class type,
    Exchange exchange,
    Object value,
    TypeConverterRegistry registry
)

// 2. Templating form of signature
@FallbackConverter
public static <T> T MethodName(
    Class<T> type,
    Exchange exchange,
    Object value,
    TypeConverterRegistry registry
)

MethodName はフォールバックコンバーターの任意のメソッド名です。

たとえば、以下の抜粋コード (File コンポーネントの実装から取得) は、GenericFile オブジェクトのボディーを変換できるフォールバックコンバーターを示し、型コンバーターレジストリーですでに利用可能な型コンバーターを利用しています。

package org.apache.camel.component.file;

import org.apache.camel.Converter;
import org.apache.camel.FallbackConverter;
import org.apache.camel.Exchange;
import org.apache.camel.TypeConverter;
import org.apache.camel.spi.TypeConverterRegistry;

@Converter
public final class GenericFileConverter {

    private GenericFileConverter() {
        // Helper Class
    }

    @FallbackConverter
    public static <T> T convertTo(Class<T> type, Exchange exchange, Object value, TypeConverterRegistry registry) {
        // use a fallback type converter so we can convert the embedded body if the value is GenericFile
        if (GenericFile.class.isAssignableFrom(value.getClass())) {
            GenericFile file = (GenericFile) value;
            Class from = file.getBody().getClass();
            TypeConverter tc = registry.lookup(type, from);
            if (tc != null) {
                Object body = file.getBody();
                return tc.convertTo(type, exchange, body);
            }
        }

        return null;
    }
    ...
}