1月25日,Apache Software FoundationのJakartaプロジェクトから,Commons 3.0がリリースされた。
CommonsはJavaアプリケーションでよく使われる機能部品を開発するプロジェクトであり,大小さまざまな機能部品ライブラリ(本稿ではコンポーネントと呼ぶ)が提供されている。
Commonsプロジェクトは,Strutsとほぼ同じころにJakartaプロジェクトに作られた。それ以前は,同じJakartaプロジェクト内で開発されていた複数のプロダクトであっても,重複する機能をそれぞれのプロジェクトにて作成していた。しかし,これでは決して潤沢とは言えないオープンソースの開発コストが重複開発によって無駄に浪費されているといっても過言ではなかった。そこで,各プロダクトから再利用可能,かつほかのプロダクトでも有益な機能を単独で利用可能な形式にパッケージングした。そして,Commonsプロジェクトを立ち上げ,それらを集約させ,再利用しやすい形態で管理することにした。
プロジェクト立ち上げ当初こそ,本格的に利用可能なコンポーネントは数個程度であったが,現在では正式にリリースされているもの,Sandboxと呼ばれる実験的に開発が行われているものなどを合わせると48個のプロダクトが存在し,ほとんどのJakartaのプロダクトが何らかのCommonsコンポーネントを利用している。Commonsの利用はJakarta内だけに留まらず,ASLで配布されているという利点から,その他のJavaオープンソースプロダクトでも利用されている。
この圧倒的な量のコンポーネント群を,アプリケーション開発において利用しない手はない。では,いったいどのようなコンポーネントが用意されているのだろう。実験的なものは安定性や仕様の成熟度といった面から,利用をおすすめできるものが少ないため,本稿では執筆時現在登録されている正式なコンポーネントの一部を紹介する(表1)。
表1●Commonsのおもなコンポーネント
コンポーネント名 | バージョン | 機能 |
BeanUtils | 1.6.1 | JavaBeansを操作するのに便利なユーティリティ・コンポーネント |
Betwixt | 1.0 Alpha 1 | XMLファイルとJavaBeanのマッピングを行うためのコンポーネント |
CLI | 1.0 | コマンドライン引数とオプションをシンプルかつ簡単に操作するためのライブラリ |
Codec | 1.2 | 一般的なエンコーダおよびデコーダ(Base64など)のコンポーネント |
Collections | 3.0 | JDK1.2のCollection APIを補完する機能を提供するコンポーネント |
Configuration | 1.0 Dev 3 | 様々なソースから設定情報を読むことができるインターフェースを提供するコンポーネント |
Daemon | 1.0 | デーモン・プログラム(Windowsではサービス)を作成するためのコンポーネント |
DBCP | 1.1 | データベース・コネクションプール・コンポーネント |
DbUtils | 1.0 | JDBCを利用するめの便利なコンポーネント |
Digester | 0.2 | XMLフォーマットのファイル(初期値設定ファイルなど)をアプリケーションで利用する場合に便利なコンポーネント |
Discovery | 1.5 | プラグ・アンド・プレイ機能をもったアプリケーションを開発する場合に必要な機能を提供するコンポーネント |
EL | 1.0 | JSP2.0のExpression Language(EL)のインタプリタ機能を提供するコンポーネント |
FileUpload | 1.0 | Webアプリケーション用のファイル・アップロードの機能を提供するコンポーネント |
HttpClient | 2.0-rc3 | JDKの java.net パッケージが提供するよりも,最新・効率的・機能豊富なHTTPクライアントの機能を実装したコンポーネント |
IO | 1.2 | ストリームやファイル・フィルタなどのコンポーネント |
Jelly | 1.0 Beta 3 | XMLフォーマットの言語を実行するインタプリタ・コンポーネント |
Jexl | Nightly | アプリケーションおよびフレームワークに組み込むためのExpression Language(EL)エンジ |
JXPath | 1.1 | XPathと呼ばれる式言語(Expression Language)のシンプルなインタプリタ・コンポーネント |
Lang | 2.0 | JDKよりも強力な文字列操作やオブジェクト操作を行う機能を提供するコンポーネント |
Latka | 1.0 Alpha 1 | HTTPリクエスト/レスポンス・レベルでのテストを行うツール |
Logging | 1.0.3 | 様々なLoggingツールをカプセル化して統一されたインタフェースで利用するためのコンポーネント |
Modeler | 1.1 | Java Management Extensions (JMX) にて定義されているMBeanを生成する仕組みを提供するコンポーネント |
Net | 1.1.0 | 各種インターネット・プロトコルのクライアント機能を提供するコンポーネント |
Pool | 1.1 | オブジェクト・プールを提供するコンポーネント |
Primitives | 1.0 | char,shortなどJavaの基本的な型のコンポーネント |
Validator | 1.0.2 | Strutsに強力な検証機能を提供するコンポーネント |
コンポーネントとフレームワークをうまく組み合わせる
高品質なアプリケーションを効率よく作成する鍵は,コードの再利用にある。なぜなら,一度作成したコードを再度利用するという事は,作成のコストが削減できるばかりではない。既に稼働実績があり品質が保障されたコードを利用するという事は,再利用部分に関しては品質が保障されており,新たに作成した部分の品質保障に注力すればよいことになる。これは,品質保証にかけるコストが同じだとすると,狭い範囲に注力した方が効果が向上するのは当然だ。
![]() |
図1●データベースとコンポーネント |
もう1つの方法であるコンポーネント(もしくはライブラリ)の再利用という方法は,古くから行われていた。ライブラリレベルでは,以前は個人や社内で蓄積し,再利用が行われていた。J2EEが生まれEJBコンポーネントという概念が生まれてからは,コンポーネントをグローバルに流通させ,より大きな単位の部品を再利用しようという動きが出てきた。
これはプラットフォーム非依存なアプリケーション開発を可能にした,Javaの功績の1つと考えられる。Javaが登場する以前は,特定のプラットフォーム向けのライブラリのバイナリを流通させるか,ソースコードで流通させるしかなかった。しかし,Javaの登場によって1つのバイナリが,すべてのJava環境で動作可能となり,アプリケーションの流通が非常に簡単になったのである。commons内のコンポーネントも当然Javaで記述されており,jarファイルとしてバイナリ形式で配布されているコンポーネントは,配置するだけで即利用が可能である。世界中の多くのアプリケーションで利用されており,品質の高いものが多い。
この2つのアプローチ(フレームワークとコンポーネント)をうまく組み合わせてアプリケーションを作成することで,目的のアプリケーションをさらに効率よく高品質に作成することが可能となるのである(図1[拡大表示])。今回紹介しているCommonsプロジェクトの各コンポーネントは,ASLで配布されており商利用を阻害せず,しかも高機能高品質なものが多いため,導入をお勧めする。
以降では,実際にどのようなコンポーネントがあり,どのように利用するのか紹介する。
Java Beanを強力に操作するBeanUtilsコンポーネント
Javaでアプリケーションを作成する場合,各コンポーネント間のデータの受け渡しにMapインタフェースを用いたりJava Beanを用いる。特に,Webアプリケーションを作成する場合には,プレゼンテーション層ではJSPとの親和性からBeanを用いることが多いだろう。しかし,モデル層へデータを渡す場合などでは,呼び出しインタフェースの統一や,プレゼンテーション層のクラス参照を避けるという観点からMapで渡す方が都合が良い。このような場合,BeanからMapおよびMapからBeanへの積み替えという,冗長な作業が発生する。この作業をコードで記述すのは非常に面倒であるばかりでなく,Bean側の属性変更(データ項目の追加・変更)のたびにコーディングしなおす必要がある。
これらのBeanに関する操作を強力にサポートするのが,commons-beansコンポーネントである。特に,BeanUtilsクラスは,BeanからMap,MapからBean,BeanからBean,といったデータのコピー機能を提供し,たった1行で記述することができる。それぞれのコピーメソッドは,コピー対象に対応する属性が存在する場合にのみ,その属性のコピーを行うため,お互いがまったく同一の属性を保持している必要はない。
リスト1●Beanから Mapへデータをコピー
SampleA abean = new SampleA();
abean.setA( "dataA");
abean.setB( "dataB");
abean.setC( "dataC");
// Bean to Map
Map map = BeanUtils.describe( abean );
System.out.println ( map.get("a" ) );
System.out.println ( map.get("b" ) );
System.out.println ( map.get("c" ) );
リスト2●Beanから Mapへデータをコピー
SampleA bean = new SampleA();
HashMap map = new HashMap();
map.put("a","dataA" );
map.put("b","dataB" );
map.put("c","dataC" );
// Map to Bean
BeanUtils.populate( bean,map);
System.out.println ( bean.getA() );
System.out.println ( bean.getB() );
System.out.println ( bean.getC() );
SampleA fromBean = new SampleA();
fromBean.setA( "dataA" );
fromBean.setB( "dataB" );
fromBean.setC( "dataC" );
SampleB toBean = new SampleB();
// Bean to Bean
BeanUtils.copyProperties( toBean,fromBean );
System.out.println ( toBean.getA() );
System.out.println ( toBean.getB() );
// SampleB には,AとBの2つの属性しかない
紹介した以外にも,BeanUtilsには,特定の属性のみを操作する機能や,数値文字列から対応するクラスを生成する機能などが提供されている。
J2SEの基本機能を強化するLangコンポーネント
J2SDKは,様々な便利はクラスを標準で用意している。しかし,実際のアプリケーション開発においては,もう一歩踏み込んだ機能が欲しくなる時がある。commons-langコンポーネントは,その要望に添えるような各種のクラスが提供されている。
筆者がよく利用するのは,StringUtilsクラスである。このクラスは頻繁に行われる文字列の操作に関する機能が提供されている。代表的なものに,isBlank,isNotBlankがある。文字型の入力データの検証を行う場合には,必ずnullか?長さ0の文字列か?を検証した後に,所定のデータであるかどうかを検証する。この検証ロジックは,非常に冗長な処理となる。しかし,StringUtilsクラスのisBlank,isNotBlankメソッドを用いることで,非常に簡単な記述で同様の検証を行うことができる。
if( StringUtils.isBlank( inputString ) ) {
System.out.println ("input String is empty");
}
このほかにも,Java言語にない機能「Enumeration」を行うためのクラスEnumが用意されている。このクラスを利用すれば,CやC++で行っていたようなEnumerationの機能が現在のJavaに簡単に実現できる(J2SE1.5からは 同様の機能が提供される予定である)。
public final class ColorEnum extends Enum {
public static final ColorEnum RED = new ColorEnum("Red");
public static final ColorEnum GREEN = new ColorEnum("Green");
public static final ColorEnum BLUE = new ColorEnum("Blue");
private ColorEnum(String color) {
super(color);
}
public static ColorEnum getEnum(String color) {
return (ColorEnum) getEnum(ColorEnum.class,color);
}
public static Map getEnumMap() {
return getEnumMap(ColorEnum.class);
}
public static List getEnumList() {
return getEnumList(ColorEnum.class);
}
public static Iterator iterator() {
return iterator(ColorEnum.class);
}
}
データベース・アクセスのもう1つの選択肢DbUtils
Javaでリレーショナル・データベースへアクセスを行う場合の選択肢は,JDBC APIを直接利用する方法,EntityBeanを利用する方法,O/R Mapperを利用する方法の3種類が一般的である。
アプリケーション要件によっては,JDBC APIを直接利用する方法が選択される場合もあるだろう。しかし,JDBC APIは必ずしも利用しやすいAPIとは言えない。なぜなら,クエリを発行するためには,SQL文の文字列を生成する必要があり,検索結果もオブジェクトとは言いがたい形式で返却されるからである。オブジェクト指向で設計,コーディングを行っているJavaのプログラム中に非オブジェクト指向な物が存在してしまうため,扱いにくいのである。しかし,JDBC APIはSQL文を直接指定できるという点がメリットでもある。EntityBeanやO/R Mapperでは行いにくいSQLのチューニングがいとも簡単に行えるし,なによりSQLに馴れた開発者には非常に直感的にアプリケーションが作成できる。
DbUtilsでは,この扱いにくいJDBC APIを簡単に扱えるような機能を提供している。例えば,検索結果をResultSetではなく,オブジェクトの配列として得たい場合には,下記のようなコードを記述すればよい。
Object[] result = (Object[]) run.query("SELECT * FROM Person WHERE name=?",
"John Doe",resultSetHander);
このほかにも,便利な機能がたくさん提供されている。JDBC APIを用いるアプリケーションを作成する場合には,利用を検討してみていただきたい。
Commonsプロジェクトには,冒頭で紹介したように,非常に多くのコンポーネントが存在し,そのすべてを紹介する事はできない。しかし,その存在と提供されているコンポーネントのおおまかな機能を知ることで,アプリケーション開発の場面できっと役に立つ時があると考える。今後,再利用可能な機能を作成する場面になった時,まず Commonsプロジェクトのコンポーネントを探してみてほしい。もしかしたら既に存在しているかもしれない。その機能が要求を十分満たしているなら,きっとCommonsコンポーネントの恩恵にあずかれるだろう。
■著者紹介
黒住幸光(くろずみ ゆきみつ)氏
株式会社アークシステム シニアコンサルタント。1989年,スーパー・コンピュータ向け言語処理環境の研究中に,生まれて間もないJavaと出会い,Javaに専念するため転職を決意。現在,株式会社アークシステムにてオープンソース・ソフトウエアを用いたWWWシステムの構築,コンサルティングを行うかたわら,雑誌への執筆,StrutsユーザーMLの管理,Ja-JakartaプロジェクトTurbine翻訳の取りまとめなど幅広く活動中。メール・アドレスは,yukimi_2@yahooo.co.jp