throws Exception

Data Format Component
Apache Camel コンポーネント開発
@ssogabe
概要
• Apache Camelがサポートするデータフォーマットを
変換するData Formatコンポーネントの仕組みを理解し、
簡単なコンポーネントを作成する。
• 題材として、Amateras Projectが提供するXLSBeansを
使用し、Excelで作成された定型フォーマットからデー
タを読み取り、JavaBeansに変換する。
Data Formatとは
• EIPにおけるMessage Translator(メッセージ変換)を
提供する仕組み
• Camelでは、コンポーネントとして、以下のコンポーネ
ントを提供
•
•
•
•
•
Bindy
JAXB
Zip
Jackson
...
使用例
• JAXBコンポーネントを使って、XML⇔JavaBeans
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<dataFormats>
<!– JAXBのオプション設定 -->
<jaxb id="myJaxb" prettyPrint="true" />
</dataFormats>
<!– JavaBeans ⇒ XML -->
<route>
<from uri="direct:start"/>
<marshal ref="myJaxb"/>
<to uri="direct:marshalled"/>
</route>
<!– XML ⇒ JavaBeans -->
<route>
<from uri="direct:marshalled"/>
<unmarshal ref="myJaxb"/>
<to uri="mock:result"/>
</route>
</camelContext>
<marshal>
<jaxb prettyPrint=“true” />
</marshal>
でもOK
XLSBeansとは
• Amateras Projectが提供する、アノテーションを付与し
たJava BeansとExcelをマッピングするライブラリ
@Sheet(name = "UserList")
public class UserList {
シートの定義
@LabelledCell(label = "title", type = LabelledCellType.Right)
public String title;
}
@HorizontalRecords(tableLabel = "User List", recordClass = User.class)
public List<User> users;
public class User {
@Column(columnName = "id")
public int id;
@Column(columnName = "name")
public String name;
UserList userList = (UserList) XMLSBeans().load(
new FileInputStream(“user.xlsx”), UserList.class)
for (User user : userList.users) {
@Column(columnName = "age")
public int age;
......
}
リストに含まれるユーザの定義
@Column(columnName = "gender")
public String gender;
}
シートをマッピング
仕様
• 変換対象となるExcelファイルは、Fileコンシューマーな
どで取得し、ExchangeのBODYに設定
• Spring XMLで、Excelファイルから変換するJavaBeans
の完全修飾名(objectType)を指定
• XLSX形式のみサポート
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:unmarshal"/>
<unmarshal>
<custom ref="xlsBeansDataFormat" />
</unmarshal>
<to uri="mock:unmarshal" />
</route>
</camelContext>
<bean id="xlsBeansDataFormat"
class=“jp.co.nttcom.eai.component.xlsbeans.XLSBeansDataFormat" >
<property name="objectType" value="jp.co.nttcom.eai.model.UserList" />
</bean>
Maven Projectの作成 (1)
• mvn archetype:generate を実行し、CamelのData Format向け
Maven Projectの雛形を生成
C:\mvn archetype:generate \
-DarchetypeGroupId=org.apache.camel.archetypes \
-DarchetypeArtifactId=camel-archetype-dataformat \
-DarchetypeVersion=2.13.2 \
-DgroupId=osseai \
-DartifactId=xlsbeans \
-Dversion=1.0-SNAPSHOT \
-Dpackage=jp.co.nttcom.eai.component.xlsbeans \
-Dname=XLSBeans \
-Dscheme=xlsbeans
• mvn eclipse:eclipse を実行し、Eclipseの.classpathなどのファイル
を作成
Maven Projectの作成 (2)
• 以下のディレクトリ、ファイルが生成される
依存ライブラリの追加
• XLSBeansを使用できるようにpom.xmlを修正
<dependencies>
(snip)
<dependency>
<groupId>jp.sf.amateras.xlsbeans</groupId>
<artifactId>xlsbeans</artifactId>
<version>1.2.5</version>
</dependency>
(snip)
</dependencies>
<repositories>
<repository>
<id>amateras</id>
<name>Project Amateras Maven2 Repository</name>
<url>http://amateras.sourceforge.jp/mvn/</url>
</repository>
</repositories>
ビルドの確認
• ビルドできることを確認する
C:\xlsbeans\mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------------------------------------------------------[INFO] Building Camel XLSBeans Data format 1.0-SNAPSHOT
[INFO] -----------------------------------------------------------------------(snip)
[INFO]
[INFO] --- maven-jar-plugin:2.3.2:jar (default-jar) @ xlsbeans --[INFO] Building jar: c:\home\tmp\xlsbeans\target\xlsbeans-1.0-SNAPSHOT.jar
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------------------------------[INFO] Total time: 13.370s
[INFO] Finished at: Wed Jul 16 10:52:18 JST 2014
[INFO] Final Memory: 7M/17M
[INFO] ------------------------------------------------------------------------
DataFormatインタフェース
• Excel⇔JavaBeansを行うメソッドを提供
• 必須のインタフェース
package org.apache.camel.spi;
public interface DataFormat {
// JavaBeans の変換 今回は実装しない
void marshal(Exchange exchange, Object graph, OutputStream stream)
throws Exception;
// Excel ⇒ JavaBeansの変換を行う
// streamは、Excelファイルのストリーム形式
Object unmarshal(Exchange exchange, InputStream stream) throws Exception;
}
ServiceSupportクラス
• コンポーネントのライフサイクルを制御するクラス
• インスタンスの生成など、初期処理、終了処理が必要な
場合は、このクラスを継承する
package org.apache.camel.support;
public abstract class ServiceSupport implements StatefulService {
// 初期処理
protected abstract void doStart() throws Exception;
}
// 終了処理
protected abstract void doStop() throws Exception;
CamelContextAwareインタフェース
• CamelContextが必要な場合に実装
• Camel本体がCamelContextをインジェクト
• 今回は使用しない
package org.apache.camel;
public interface CamelContextAware {
// CamelContextの設定
void setCamelContext(CamelContext camelContext);
// CamelContextの取得
CamelContext getCamelContext();
}
XLSBeansDataFormatの実装(1)
• パラメータはフィールドとして定義
• objectTypeの確認とXLSBeansのインスタンスを生成
public class XLSBeansDataFormat extends ServiceSupport implements DataFormat {
private Class objectType; // setter/getterは必要
private XLSBeans xlsBeans;
@Override
protected void doStart() throws Exception {
// objectTypeは必須
ObjectHelper.notNull(objectType, "objectType");
xlsBeans = new XLSBeans();
}
@Override
protected void doStop() throws Exception {
// do nothing
}
XLSBeansDataFormatの実装(2)
• unmarshalのみ実装
• XLSBeansのインスタンスは、スレッドで共有しても問
題ないので、先に生成しておく
@Override
public void marshal(Exchange exchange, Object graph, OutputStream stream)
throws Exception {
}
throw new UnsupportedOperationException("Not supported yet.");
@Override
public Object unmarshal(Exchange exchange, InputStream stream)
throws Exception {
}
}
// XLSX形式のみ対応(固定)
return xlsBeans.load(stream, objectType, WorkbookFinder.TYPE_XSSF);
テストライブラリの追加
• Camelでは、いくつかのテストをサポートするライブラ
リを提供
• Spring XMLで作成したルートをテストするために、
pom.xmlに以下を追加
<dependencies>
(snip)
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring</artifactId>
<version>2.13.2</version>
<scope>test</scope>
</dependency>
(snip)
</dependencies>
テスト資材の配置 (1)
• テスト対象のExcelファイルuserlist.xlsxを、
src\test\resources\jp\co\nttcom\eai\component
\xlsbeansに配置
タイトル部分(背景が水色)は
変更しないこと
シート名
は”UserList”
テスト資材の配置 (2)
• src\test\jp\co\nttcom\eai\modelに配置
• Excelシートをマッピングするクラスを作成
package jp.co.nttcom.eai,model;
(snip)
シート名
@Sheet(name = "UserList")
public class UserList {
内容が”title”のセルの右側の
セルの値をtitleに設定
@LabelledCell(label = "title", type = LabelledCellType.Right)
public String title;
}
@HorizontalRecords(tableLabel = "User List", recordClass = User.class)
public List<User> users;
“User List”のセルの下にある表の行の内
容をUserクラスにマッピングする
テスト資材の配置 (3)
• “User List”テーブルの1行の情報をマッピングするUserク
ラスを作成
package jp.co.nttcom.eai,model;
(snip)
public class User {
“id”列の値をマッピング
@Column(columnName = "id")
public int id;
@Column(columnName = "name")
public String name;
@Column(columnName = "gender")
public String gender;
@Column(columnName = "age")
public int age;
}
テストするルートの作成
• テストするルートは、”テストクラス名”-context.xml
• テストクラス名がXLSBeansDataFormatTest.javaの場合は、
XLSBeansDataFormatTest-context.xml
• Excelシートと同じディレクトリに配置する。
<?xml version="1.0" encoding="UTF-8" ?>
<beans ... >
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:unmarshal"/>
<unmarshal>
<custom ref="xlsBeansDataFormat" />
</unmarshal>
<to uri="mock:unmarshal" />
</route>
</camelContext>
<bean id="xlsBeansDataFormat"
class=“jp.co.nttcom.eai.component.xlsbeans.XLSBeansDataFormat" >
<property name="objectType" value="jp.co.nttcom.eai.model.UserList" />
</bean>
</beans>
テストクラスの作成(1)
• 作成したExcelファイル、ルートを使用したテストクラ
ス(XLSBeansDataFormatTest)を作成
package jp.co.nttcom.eai.component.xlsbeans;
(snip)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class XLSBeansDataFormatTest {
@Produce(uri = "direct:unmarshal")
private ProducerTemplate template;
@EndpointInject(uri = "mock:unmarshal")
private MockEndpoint mock;
:
extends ... は不要
テストルートの”<from
uri=“direct:unmarshal”/>”に
データを送付するためのオブジェクト。
インジェクトされる
テストルートの
” <to uri=“mock:unmarshal” />”のモック
テストクラスの作成(2)
• <to uri=“mock:unmarshal” />が受け取るBODYが、
UserListのインスタンスであることなどを確認
@Test
public void testUnmarshal() throws Exception {
// 期待値の設定
// Exchangeを1つ受け取り、BODYがUserListのインスタンスであること
mock.expectedMessageCount(1);
mock.message(0).body().isInstanceOf(UserList.class);
// テスト用のExcelファイルを、BODYとして送信する
String fileName = getClass().getResource("userlist.xlsx").getFile();
template.sendBody(new File(fileName));
// 期待値を満たしていることを確認
mock.assertIsSatisfied();
}
// BODYを取得して、タイトル、Userの数を確認
UserList userList =
mock.getReceivedExchanges().get(0).getIn().getBody(UserList.class);
assertThat(userList.title, is("ユーザリスト"));
assertThat(userList.users, hasSize(5));
演習1 Excel形式の指定
• 入力のExcel形式(bookType)を選択可能とする。
• “HSSF” Excel 2003形式 拡張子 .xls (Apache POI)
• “XSSF” Excel 2007形式 拡張子 .xlsx (Apache POI)
• “JXL” Excel 2003形式 拡張子 .xls (Java Excel API)
@Override
ここを設定可能とする
public Object unmarshal(Exchange exchange, InputStream stream)
throws Exception {
}
}
// XLSX形式のみ対応(固定)
return xlsBeans.load(stream, objectType, WorkbookFinder.TYPE_XSSF);
演習2 XLSBeansConfig
• 読み込んだ文字列のトリムなど、読み込み時の動作をカ
スタマイズ可能とする
XLSBeans xlsBeans = new XLSBeans();
// 読み込み時の動作を設定する情報
XLSBeansConfig config = new XLSBeansConfig();
// 文字列をtrim
config.setTrimText(trim);
xlsBeans.setConfig(config);
<bean id="xlsBeansDataFormat"
class=“jp.co.nttcom.eai.component.xlsbeans.XLSBeansDataFormat" >
<property name="objectType"
value=“jp.co.nttcom.eai.component.xlsbeans.UserList" />
<property name="bookType" value="XSSF" />
<property name="config" ref="config" />
</bean>
<bean id="config" class="net.java.amateras.xlsbeans.XLSBeansConfig" >
<property name="trimText" value="true" />
</bean>
モデラーでのコンポーネント
• Spring XMLをもとに、モデラーのコンポーネントをどの
ように提供するか検討する
<camelContext xmlns="http://camel.apache.org/schema/spring">
データ変換として提供
<route>
<from uri="direct:unmarshal"/>
<unmarshal>
<custom ref="xlsBeansDataFormat" />
</unmarshal>
<to uri="mock:unmarshal" />
共有できるので、データ変換
</route>
</camelContext>
のオプションとして提供
objectTypeはパラメータ
<bean id="xlsBeansDataFormat"
class=“jp.co.nttcom.eai.component.xlsbeans.XLSBeansDataFormat" >
<property name="objectType" value="jp.co.nttcom.eai.model.UserList" />
</bean>