japan.internet.comThe Internet & IT Network
RSS
  • ニュース
  • コラム
  • リサーチ
  • ヘッドライン
  • 特集
  • ブログ
  • プレスリリース
  • 専門チャンネル
  • イベント
  • ランキング
  • ニュースメール
2008年10月7日
文字サイズ文字サイズ小文字サイズ中文字サイズ大
デベロッパー2006年3月22日 10:00

Jakarta Commonsを使ってJDKクラスを拡張する:パート2

海外海外internet.com発の記事
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • Buzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!

はじめに

 Jakarta CommonsはJakartaのさまざまなプロジェクトで使われている再利用可能なクラスの集まりですが、独立したコンポーネントとして自分のJavaプロジェクトの中でも利用できます。本稿はJakarta Commonsのさまざまなコンポーネントを紹介する3回シリーズの第2回です。このシリーズでは実際のサンプルアプリケーションを通じてJakarta Commonsコンポーネントの使い方を説明しています(前回の記事を読むには、ここをクリック)。これらのサンプルは、Jakarta Commonsコンポーネントを例示するだけのものではなく、各自のJavaプロジェクトで再利用できる完全なアプリケーションです。

 本稿では次のコンポーネントを取り上げます。

  • Codec
  • DBCP
  • DBUtils
  • Email
  • i18n

 本稿には完全なソースコードが付属しています。ソースコードを実際に試すには、ダウンロードしたzipファイルをローカルドライブに展開し、各サンプルのテストケースをJUnitで実行してください。

著者注
 Commonsコンポーネントのアーキテクチャと用法を理解するためには、オブジェクト指向プログラミング(OOP)とGang of Fourのデザインパターン(Command、Decorator、Singleton、Factory、Chain of Responsibility、Composite)についての基本的知識が非常に役立ちます。

Codec

 Commons Codecには、フォネティックエンコーダ、HexおよびBase64エンコーダ、URLエンコーダといった、一般的なエンコード/デコードアルゴリズムが含まれています。フォネティックエンコーダは言語エンコーダで、検索エンジン、スペルチェック関数、デジタル辞書などのアプリケーションで利用できます。HexおよびBase64エンコーダは、文字を使ってバイナリデータを表現するアプリケーションで利用できます。URLエンコーダはさらにいくつかの機能を持っており、JDKのURLEncoderおよびURLDecoderクラスの代替と考えられます。

 このコンポーネントにはDigestUtilsクラスも含まれており、SHAおよびMD5ダイジェストを作成するのに利用できます。次のセクションで、これらのクラスの使い方を実際のサンプルで示します。

言語エンコーダ

 フォネティックアルゴリズムは発音が似ている単語を判別するのに使われます。入力された単語に対して代替案を提示するワープロアプリケーションが格好の例と言えるでしょう。Commons Codecには、SoundexMetaphoneRefinedSoundexDoubleMetaphoneという4つのクラスが含まれています。各クラスは別々のアルゴリズムを使って、ある単語の発音が別の単語と似ているかどうかを判別します。これらのアルゴリズムの説明を読むと、MetaphoneのほうがSoundexよりも正確だとわかります。

 最初のサンプルアプリケーションでは、似通った単語から綴りの誤った単語を判定するためにSoundexクラスを使用しています。また、Strategyデザインパターンを使ってアルゴリズムを選択するようになっているので、他の3つのクラスのアルゴリズムをサポートするようにアプリケーションを修正することも可能です(このアプリケーションのクラスはソースコードの「src」フォルダ内のパッケージ「in.co.narayanan.commons.codec」に収められています)。

 「words.txt」ファイルには短い単語リストが含まれています。Wordsクラスは、このファイルからの単語リストのロードを抽象化しており、IWordsインターフェイスに従っています。WordsAssistantはアプリケーションのエントリポイントクラスです。このクラスはSoundexアルゴリズムの1つを使って似通った単語を調べ、IWordsインターフェイスによって、それらの単語にアクセスします。リスト1はWordsAssistantクラス内のgetSimilarWordsメソッドの実装です。これはSoundexStrategyクラスからストラテジを選び、単語を繰り返し調べて一致するものを探します。このとき、ISimilarWordStrategyインターフェイス内のisSimilarメソッドを呼び出して一致するものを判別します。それから一致する単語をリストに追加し、呼び出し元に返します。

リスト1 単語データベースの反復的に調べて似通った単語を見つける
ISimilarWordStrategy strategy =
    SoundexStrategy.getStrategy(type);

List<String> similarWords = new ArrayList<String>();

// Iterate the words and append similar words to the list
// and return
Iterator<String> wordsList = words.getWords().iterator();
String fileWord;
while(wordsList.hasNext()) {
    fileWord = wordsList.next();
    try {
        if(strategy.isSimilar(searchWord, fileWord)) {
            similarWords.add(fileWord);
        }
    } catch (WordsAssistantException e) {
        throw new WordsAssistantException(
            "Unable to determine similar words", e);
    }
}
return similarWords; 

 SoundexStrategyクラスとCharDiffStrategyクラスはISimilarWordStrategyインターフェイスに実装を提供し、CommonsのSoundexクラスを使用します。リスト2はISimilarWordStrategyインターフェイスの定義です。このインターフェイスにisSimilarメソッドを実装すれば、新しいストラテジをサンプルアプリケーションにプラグインすることができます。

リスト2 アルゴリズムの選択を可能にするインターフェイス
public interface ISimilarWordStrategy {
    boolean isSimilar(String word1, String word2)
        throws WordsAssistantException;
}

 リスト3では、org.apache.commons.codec.language.Soundexクラス内のsoundexメソッドが単語間での発音の類似性を調べます。このメソッドは似通った単語について同一となるコードを返し、それらを比較して単語どうしが似ているかどうかを判定します。たとえば、「compont」「component」「compenent」という単語なら、このコードはA515になります。

リスト3 Soundexアルゴリズム
private static class SoundexStrategy extends SimilarWordStrategy {

    public boolean isSimilar(String word1, String word2)
        throws WordsAssistantException {
        return soundex.soundex(word1).equals(
            soundex.soundex(word2));
    }

}

 リスト4では、org.apache.commons.codec.language.Soundexクラス内のdifferenceメソッドが0〜4の数を返します。4は一致度が最も高く、0は一致度が最も低いことを表します。このサンプルではピボットを2に設定しています。JUnitテストケースクラスTestLanguageEncodersはメインクラスメソッドgetSimilarWordsを呼び出すことにより、このアプリケーションのデモを実行します。

リスト4 類似性を調べる別の方法
private static class CharDiffStrategy
    extends SimilarWordStrategy {

    private static final int DIFF_RANGE = 2;

    public boolean isSimilar(String word1, String word2)
        throws WordsAssistantException {

        try {
            return (soundex.difference(word1, word2) > DIFF_RANGE) ?
                true : false;
        } catch (EncoderException e) {
            throw new WordsAssistantException(
                "Unable to determine the similarity", e);
        }
    }
}

バイナリエンコーダ

 バイナリエンコーダはバイナリデータをASCII形式で伝送するのに利用できます。たとえば、XMLに格納されたデジタル名刺にイメージを添付する必要がある場合、バイナリエンコーダはアルゴリズムの1つを使ってイメージのバイナリデータをエンコードし、それを専用のタグでXMLファイルに追加することができます。

 「org.apache.commons.codec.binary」パッケージには、Base64BinaryCodecHexというクラスが含まれており、それぞれがバイナリデータの異なるエンコード方法を表現しています。サンプルアプリケーションではBase64アルゴリズムを使用しており、これによりバイナリファイルをエンコードしてXMLに格納します。XMLファイルには、検索可能な名前/値ペアの形式でデータを記述したメタデータが含まれます。

 in.co.narayanan.commons.codec.WrapItクラスは、このサンプルで使用する唯一のクラスです。これはバイナリファイルをエンコードし、XMLコンテンツとともにメタデータを作成します。リスト5は、このクラスによって生成されるサンプルXMLです。

リスト5 WrapItクラスによって生成されるサンプルXML
<data>
    <meta-data>
        <entry name=’keywords’ value=’Image, Personal, Face, Profile’/>
        <entry name=’filename’ value=’test.bmp’/>
        <entry name=’author’ value=’Narayanan A R’/>
    </meta-data>
    <binary>
    Qk0+QwAAAAAAADYAAAAoAAAASQAAAE4AAAABABgAAAAAAAhDAAAAAAAAAAAAAA
DLMjC7MhCLEZB7AWBK8QAa0LAX0HAUUDla6Y7vLw7vLw7vLw7vHx7vHx7vHx7vHx7v
Hx7vHx7fHw7fHw7fHw7fHx7fHx7fHx7vHw7vHw7vHw7fLw7fLw7fLw7fHx7fHx7fHx
AO7y8e7y8e7y8e/xPv+WPv+WPv+WPv+WPv+WPv+WPv+WPv+WPv+WPv+WPgA=
    </binary>
</data>

 エンコードされたバイナリデータは<binary>タグで囲まれています。メタデータは一連の<entry>タグとして表現されています。バイナリコンテンツをエンコードしてXMLファイルに格納すると、インターネットでの伝送が簡単になります。たとえば、処理のためにWebサービスレイヤに送られるXMLのVisaアプリケーションフォームで、イメージや、履歴書、卒業証明書などのバイナリコンテンツを伝送することができます。

 リスト6はWrapItクラスから抜粋したコードで、これにより一度に1,024バイトを読み取って実際のエンコードを行います。ファイルから読み取った最後のデータ群に対して一度だけtruncateBytesメソッドを呼び出します。encodeBase64Chunked静的メソッドはエンコードされたコンテンツを76文字のブロックに分割して可読性を高めます。メタデータはXML形式のデータを検索可能できるようにします。

リスト6 バイナリコンテンツのエンコードにBase64を使用しているコード
while((bytesRead=inputStream.read(binaryData)) != -1) {
    if(bytesRead < 1024) {
        encodedBinaryData = 
            Base64.encodeBase64Chunked(
                truncateBytes(binaryData, bytesRead));
    } else {
        encodedBinaryData = Base64.encodeBase64Chunked(binaryData);
    }
    encodedData.write(encodedBinaryData);
}

URLエンコーダ

 org.apache.commons.codec.net.URLCodecクラスは、文字列、オブジェクト、またはバイト配列に対するwww-form-urlencodedエンコード方式を実装しています。このクラスは、JDKのURLEncoderクラスと次の点で異なります。

  • 指定された文字セットに対してエンコードとデコードを実行できる。
  • 文字列に加え、オブジェクトやバイト配列にも有効。

 このクラスのサンプルはありません。javadocのURLCodecを見れば容易に理解できるからです。

DBCP

 DBCP(Database Connection Pool)コンポーネントは、JDBCリソースをプールする必要のあるアプリケーションで利用できます。このコンポーネントは、JDBC接続とは別にStatementインスタンスとPreparedStatementインスタンスのサポートも提供します。

 JakartaのWebサイトにあるDBCPのドキュメントの説明は包括的で役に立ちます。特にユーザーガイドのDBCP Wikiサイトシーケンス図は一見の価値があります。「org.apache.commons.dbcp」パッケージのjavadocには、APIに関する詳細な情報も含まれています。DBCPを使用するようにTomcatを設定する方法については、Jakarta-commons Wikiを参照してください。

 このセクションのサンプルアプリケーションでは、軽量データベースエンジンHypersonic SQLを使ってDBCPの機能を例示しています。このアプリケーションの主な目的は、名前や電子メールアドレスなどの連絡先情報を格納して取り出すことにあります。このアプリケーションでは、DBCPフレームワークが提供するプリペアードステートメントとプールされたJDBC接続を使用します。ソースファイルはソースコードの「src」フォルダ内のパッケージ「in.co.narayanan.commons.dbcp」に収められています。

 このアプリケーションでは以下のクラスを使用しています。

ContactInfo連絡先の詳細を表現するドメインオブジェクト。
ContactInfoRepositoryデータベースから連絡先レコードを作成するcreateContactInfoメソッドや、連絡先を検索するfindByNameメソッドなどが含まれているDAOクラス(このクラスに、プールされた接続およびステートメントの取得元となるIDatabaseContextの実装クラスへの参照が与えられます)。
DatabaseContextDBCPを使用してプールされた接続とステートメントをDAOクラスに供給します。

 TestDBCP JUnitテストケースクラスを実行してアプリケーションを起動してください(アプリケーションの実行中は「hsqldb.jar」をクラスパスに入れておく必要があります)。このクラスはDatabaseContextを初期化し、ContactInfoRepositoryに参照を渡して、連絡先の詳細を格納します。リスト7はDBCPフレームワークを初期化するためのコードです。

リスト7 DBCPフレームワークを初期化する
// Load the HSQL Database Engine JDBC driver
// hsqldb.jar should be in the class path
try {
    Class.forName("org.hsqldb.jdbcDriver");
} catch (ClassNotFoundException e) {
    throw new DbException("Unable to load the db driver", e);
}

Properties db = getDBProperties();
ConnectionFactory connectionFactory =
        new DriverManagerConnectionFactory(db.getProperty("url"),
                                           db.getProperty("user"),
                                           db.getProperty("password"));

connectionPool = new GenericObjectPool();
KeyedObjectPoolFactory stmtPool =
    new GenericKeyedObjectPoolFactory(null);
new PoolableConnectionFactory(
    connectionFactory,connectionPool,stmtPool,null,false,true);
dataSource = new PoolingDataSource(connectionPool);

 DBCPは接続とステートメント参照をプールするためにCommons Poolフレームワークを使用します。このフレームワークは汎用性があって、任意のオブジェクトをプールできます。PoolableConnectionFactoryクラスはインスタンス化の際にコンストラクタに渡されたGenericObjectPoolインスタンスに自分自身を登録します。このファクトリクラスはJDBC接続の新しいインスタンスを作成するのに使われます。

 接続プール参照をPoolingDataSourceに渡す必要があります。PoolingDataSourceクラスはjavax.sql.DataSourceインターフェイスを実装して、プールされた接続を提供します。DAOクラスはdataSource変数を使用して接続を取得することができます。

 KeyedObjectPoolFactoryクラスのインスタンスをPoolableConnectionFactoryコンストラクタに渡すと、JDBC接続が作成するプリペアードステートメントオブジェクトのインスタンスをプールすることができます。KeyedObjectPoolFactoryはプールファクトリの変形で、リソースがキーごとにプールされます。プリペアードステートメントの場合、クエリがキーとして機能します。したがって、同じクエリの呼び出しに対してプールされたプリペアードステートメントインスタンスが返されます。

 リスト8はDAOクラス内のごく一般的なメソッドです。7〜9行目でDatabaseContextから接続を取得し、それを使ってデータベースにレコードを挿入します。

リスト8 プールされた接続とPreparedStatementオブジェクトの使用
String query = "INSERT INTO contactinfo(name,email) VALUES(?,?)";
Connection con = null;

PreparedStatement createContactPrStmt = null;
try {
    try {
        con = context.getConnection();
        createContactPrStmt =
            context.getPreparedStatement(con, query);
        createContactPrStmt.setString(1, contactInfo.getName());
        createContactPrStmt.setString(2, contactInfo.getEmailAddress());
        createContactPrStmt.execute();
    } finally {
        if(createContactPrStmt != null) {
            createContactPrStmt.close();
        }
        if(con != null) {
            con.close();
        }
    }
} catch(SQLException e) {
    throw new DbException("Unable to create ContactInfo record", e);
} catch(DbException e) {
    throw new DbException("Unable to create ContactInfo record", e);
}

 DBCPで使われるプールはカスタマイズ性が高いので、エンタープライズソフトウェアに適しています。

DBUtils

 DBUtilsフレームワークはJDBC APIを使いやすくする軽量ラッパーです。これにより、開発者はデータの照会と更新に専念し、リソースのクリーンアップをフレームワークに任せることができます。

 このコンポーネントはデータアクセスオブジェクトでの使用にうってつけです。持続性のためにHibernateJDOを使用する必要はないが、ダイレクトJDBC呼び出しを必要とするアプリケーションでは、DBUtilsを使用すると開発効率が上がります。Jakartaのサイト内の「Overview」「Examples」は非常に包括的です。このフレームワークに含まれているクラスは多くないので、javadocも読みやすいです。

 DBUtilsはどのような働きをするのでしょうか。SQLクエリのSELECTINSERTDELETE、またはUPDATEQueryRunnerクラスのqueryメソッドに渡すことができます。クエリに加えて、ResultSetHandlerインターフェイスの実装も渡します。SELECTクエリが実行されると、取得したResultSetオブジェクトがハンドラに渡されます。ハンドラはレコードを読み取り、ドメインオブジェクトを作成し、呼び出し元に返すことができます。作成されたドメインオブジェクトはqueryメソッドの呼び出し元に返されます。

 このフレームワークには、ArrayHandlerArrayListHandlerBeanHandlerBeanListHandlerColumnListHandlerKeyedHandlerMapHandlerMapListHandlerScalarHandlerなどのハンドラがあります。しかし、ResultSetHandlerを実装することで、特定の状況に使用する独自のハンドラを書くこともできます。

 以降では、このフレームワークの例を示すために、DBCPフレームワークのサンプルアプリケーションの修正版を紹介します。DatabaseContextクラスとContactInfoRepositoryクラスが、DBUtilsフレームワークを使用するように修正されています。リスト9はこれらの修正のコードです。このコードは、DBCPフレームワークによって返されるDataSourceインスタンスの参照をQueryRunnerコンストラクタに渡します。したがって、クエリーランナーのインスタンスをシステム内のすべてのクラスで利用できます(ソースファイルはソースコードの「src」フォルダ内のパッケージ「in.co.narayanan.commons.dbutils」に収められています)。

リスト9 DBUtilsを使用するためにDatabaseContextクラスに加えられた修正
public class DatabaseContext implements IDatabaseContext {
    // Other declarations
    private QueryRunner qRunner;
 
    public DatabaseContext() throws DbException {
        init();
    }
 
    private void init() throws DbException {
        // Initializing for DBCP
        dataSource = new PoolingDataSource(connectionPool);
        qRunner = new QueryRunner(dataSource);
    }
 
    // Other public and private methods
    public QueryRunner getQueryRunner() {
        return qRunner;
    }
}

 リスト10はContactInfoRepositoryクラスからのコードで、QueryRunnerを使ってデータベースにレコードを挿入する方法を示しています。updateメソッドはSQLクエリとそのパラメータを受け取り、それらをプリペアードステートメントに渡します。ここで注目すべきことは、リソースのクリーンアップをDBUtilsフレームワークがやってくれるので、GUIでユーザーにエラーを表示するためにSQLExceptionだけを処理すればよいという点です。

リスト10 QueryRunnerを使ってレコードを挿入する
public void createContactInfo(ContactInfo contactInfo)
    throws DbException {

    String query = "INSERT INTO contactinfo(name,email) VALUES(?,?)";
    try {
        context.getQueryRunner().update(query,
            new Object[] {contactInfo.getName(),
            contactInfo.getEmailAddress()});
    } catch(SQLException e) {
        throw new DbException(
            "Unable to create ContactInfo record", e);
    }
}

 リスト11では、ResultSetHandlerインターフェイスの実装を作成し、QueryRunner内のqueryメソッドにインスタンスを渡します。データベースからレコードを選択するためにプリペアードステートメントのパラメータも渡します。このリストにはカスタムのResultSetHandlerも含まれており、その使用方法が示されています。より簡単なアプローチは、BeanHandlerを使用し、リフレクションを使ってContactInfoドメインオブジェクトに自動的に値を入れるというものです。

リスト11 QueryRunnerを使ってデータベースからレコードを選択する
private static class ContactInfoRSHandlerByName
    implements ResultSetHandler {

    public Object handle(ResultSet results)
        throws SQLException {
        return getContactInfo(results);
    }

    private ContactInfo getContactInfo(ResultSet results)
        throws SQLException {

        if(results.next()) {
            ContactInfo contact = new ContactInfo();
            contact.setName(results.getString("name"));
            contact.setEmailAddress(results.getString("email"));
            return contact;
        } else {
            return null;
        }
    }
}

public ContactInfo findByName(String name) throws DbException {
    String query = "select name, email from contactinfo where name=?";
    try {
        return (ContactInfo)context.getQueryRunner()
            .query(query,new Object[] {name},
            new ContactInfoRSHandlerByName());
    } catch(SQLException e) {
        throw new DbException(
            "Unable to find ContactInfo record by name", e);
    }
}

Email

 Commons Emailは、Javaアプリケーションでの電子メールの送信を簡単にするいくつかのクラスから構成されています。Jakartaのサイト内の「Overview」と「Samples」にも有用な情報が含まれています。

 このセクションのサンプルには、Commons Emailを使って書かれたコマンドラインツールが含まれています。これは単純なメッセージを送信するツールで、電子メールを手早く送信するのにとても便利です。このツールをさらに拡張すれば、HTML形式のメッセージや添付ファイルもサポートすることもできます。このサンプルアプリケーションでは、クラスパスにJavaMailライブラリJavaBean Activation Frameworkライブラリを入れておく必要があります(完全なソースはソースコードの「src」フォルダ内のパッケージ「in.co.narayanan.commons.email」に収められています)。

 このツールのデザインには以下のクラスが含まれています。

  • CommandlineParser ― このクラスは、SimpleMailCommandクラスのインスタンスを作成するためにコマンドラインに渡された引数を解析します。SimpleMailCommandはコマンド引数とCommons EmailクラスのSimpleEmailをカプセル化しています。SimpleMailCommandは後でメールを送信するためにメインクラスのEmailBuddyで使用されます。リスト12は単純なメールを送信するために必要な引数を列挙したものです。
  • リスト12 単純なメールを送信するためのCLI構文
    java EmailBuddy -host mail.mysite.com
                    -user user
                    -password password
                    -to "a@somewhere.org, b@somewhere.org"
                    -from me@mysite.com
                    -subject "Commons Mail"
                    -message "Commons site is really good and
     interesting. You guys have to try it out"
    
    リスト13はCommons Email APIの用法を示しています。
    リスト13 単純なメールメッセージを送信するためのCommons Emailの使用例(Jakartaのサイトから)
    SimpleEmail email = new SimpleEmail();
    email.setHostName("mail.myserver.com");
    email.addTo("jdoe@somewhere.org", "John Doe");
    email.setFrom("me@apache.org", "Me");
    email.setSubject("Test message");
    email.setMsg("This is a simple test of commons-email");
    email.send();
    
  • SimpleMailCommand ― このクラスはSimpleEmailクラスをラップし、コマンドライン引数を解析してインスタンスを作成します。
  • EmailBuddy ― これは残りのクラスを統括するメインクラスです。

 デザインは全体として次のような流れになります。

  • EmailBuddyがコマンドライン引数をCommandlineParserに委ねます。
  • CommandlineParserSimpleMailCommandを使ってSimpleEmail Commons Emailクラスを解析し値を入れます。
  • EmailBuddySimpleEmail参照を取得し、送信メソッドを呼び出してメッセージをディスパッチします。

 このツールを拡張して、たとえばHTML形式のメッセージを送信できるようにするには、HtmlMailCommandクラスを書く必要があります。これはコマンドライン引数を解析し、HtmlEmail Commons Emailクラスに値を入れるためのものです。また、CommandlineParserを修正して、HtmlMailCommandを使うようにします。

i18n

 Jakartaはまだi18nをリリースしていませんが、ソースを入手してビルドするか、スナップショットビルドをダウンロードすることができます。ソースをダウンロードするには、Subversionクライアントが必要です。筆者はSmartSVNを使ってこれを取得しました。ここをクリックすると、クイックスタートガイドを参照できます。

 Commons i18nは、スローする例外や表示するエラー/成功メッセージを特定の地域に合わせてローカライズする必要のあるJavaアプリケーションで使用します。メッセージはXMLまたはプロパティファイル形式で格納することができます。XMLの場合、すべてのロケールに関係するメッセージを一緒に格納できるという利点があります。

 このセクションのサンプルアプリケーションでは、データベースにアクセスするアプリケーションでロケール固有のエラーをどのようにスローして処理するかを例示しています。筆者はメッセージの格納にXMLを使用しました。Commons i18nライブラリの「commons-i18n-0.3.jar」をクラスパスに入れておく必要があります。また、アプリケーションの起動にはTestI18n JUnitテストケースクラスを実行する必要があります。ソースコードは、ソースコードの「src」フォルダ内のパッケージ「in.co.narayanan.commons.i18n」に収められています。

 最初のステップでは、システム内のすべてのアプリケーション例外がjava.lang.Exceptionではなくorg.apache.commons.i18n.LocalizedExceptionクラスを拡張するようにします。リスト14は、このサンプルアプリケーションで使用するアプリケーション例外クラスです。

リスト14 java.lang.ExceptionではなくLocalizedExceptionを拡張するアプリケーション例外
public class DAOException extends LocalizedException {
 
    public DAOException(
        ErrorBundle errorMessage, Throwable throwable) {

        super(errorMessage, throwable);
    }
 
    public DAOException(ErrorBundle errorMessage) {
        super(errorMessage);
    }
}

 次のステップでは、ロケール固有のエラーメッセージを作成します。リスト15は、このサンプルで使用するXMLファイルです。

リスト15 ロケール固有のエラーメッセージ
<?xml version="1.0" encoding="UTF-8" ?>
<messages>
  <message id="insertionfailed">
    <locale language="en">
      <entry key="title">Database insertion failed</entry>
      <entry key="text">Unable to insert row value {0} to the database
      </entry>
      <entry key="summary">Summary: Database insertion error</entry>
      <entry key="details">The given value cannot be inserted
 since the database has reported an error</entry>
    </locale>
    <locale language="fr">
      <entry key="title">French - Database insertion failed</entry>
      <entry key="text">French - Unable to insert row value {0}
 to the database</entry>
      <entry key="summary">French - Summary: Database insertion error
      </entry>
      <entry key="details">French - The given value cannot be
 inserted since the database has reported an error</entry>
    </locale>
  </message>
</messages>

 すべてのロケールメッセージが同じXMLファイルにまとめられていることに注目してください。Frenchロケールのメッセージには「French」という接頭辞が付いているので、実行時に一目でわかります。

 次のステップでは、メッセージをロードするクラスを作成します。これを示しているのがリスト16のコードです。ここでは新しいメッセージプロバイダを追加し、それをキー"dao-errors"に対して格納しています。

リスト16 XMLファイルからメッセージをロードするヘルパークラス
public class ErrorHelper {
    static {
        // Load the message bundle from the XML file
        MessageManager.addMessageProvider("dao-errors",
            new XMLMessageProvider(ErrorHelper.
            class.getResourceAsStream("exceptions.xml")));
    }
 
    public static ErrorBundle getErrorBundle(
        String bundleKey, String arg0) {

        return new ErrorBundle(
            "dao-errors", bundleKey, new Object[] {arg0});
    }
}

 最後のステップでは、作成したアプリケーション例外クラスを使ってシステム内のエラーを示します。リスト17では、DAOExceptionのインスタンスが作成され、ErrorBundleインスタンスへの参照が渡されます。Commons i18nフレームワークは指定されたメッセージキーに対応するメッセージをMessageManagerから取得し、ErrorBundleクラスに送ります。この例では、キーinsertionfailedに関係するメッセージが設定されます。

リスト17 ロケール固有のエラーをスローするためにCommons i18nを使用するDOAクラス
public void createCustomer(String name) throws DAOException {
    Connection con = null;
    Statement createStmt = null;
    try {
        // Create a customer
    } catch (SQLException e) {
        throw new DAOException(ErrorHelper.getErrorBundle(
            "insertionfailed", name), e);
    }
}

 アプリケーションのプレゼンテーションレイヤ内の実際のメッセージを取得したり、それをログに書き込んだりするには、例外オブジェクト内に設定されたErrorBundleにアクセスします。

 Commons i18nを使用すると、アプリケーションのデザインが改善されて、グローバル化が容易になります。

まとめ

 Jakarta Commonsについて紹介するシリーズ第2回では、次のことを取り上げました。

  • Commonsのさまざまなコンポーネントが提供する豊富な機能
  • ユーティリティクラスの特定のコンポーネントやメソッドの具体的な用途
  • さまざまなAPIのパッケージとクラスの概要

 これからJavaアプリケーションをデザインしたり開発したりするときは、有用なクラスを選んで適切に使用できるのではないでしょうか。このシリーズの第3回でも、引き続き注目すべきコンポーネントについて解説します。

著者紹介

Narayanan A.R.(Narayanan A.R.)
テスト駆動開発、アジャイル方法論、Javaテクノロジ、およびデザインパターンの熱烈な擁護者。Javaテクノロジを使ったソフトウェアのデザインおよび開発に携わって数年になる。
関連テーマ
最新トップニュース
Graphic Design Forum
【Graphic Design Forum】
コメントをお寄せいただいた方々へ (10月7日)
データメーション
【データメーション】
eBayのやり口(10月7日)
ベンチャー専門家の目利きブログ「なぜこの企業は伸びるのか?」
【ベンチャー専門家の目利きブログ「なぜこの企業は伸びるのか?」】
「プロの営業マンを社会に輩出していく!!」/株式会社A・R・M(10月6日)
エンジニアの独り言
【エンジニアの独り言】
得体の知れない情報(?)との向き合い方(9月17日)
最新テクノロジーの意外な処方箋
【最新テクノロジーの意外な処方箋】
昆虫と退屈なことについて(9月16日)
「IT の耳」
「IT の耳」
【書評】ニコ動から RMT まで〜『人はなぜ形のないものを買うのか―仮想世界のビジネスモデル』(10月7日)
DevX
DevX
アジャイルソフトウェアプロジェクトを管理する(10月7日)
エンジニア転職ノウハウ開発室
エンジニア転職ノウハウ開発室
SEって、デジタル製品は判官びいきで選ぶよね?(10月7日)
アイレップの SEM フロンティア
アイレップの SEM フロンティア
フル CSS でサイト構築をする SEO のメリット(10月7日)
百式のネットビジネス研究
百式のネットビジネス研究
YouTube の動画に吹き出しで台詞を入れられる「TubePopper」(10月7日)
モバイルSEO@フラクタリスト
モバイルSEO@フラクタリスト
応用的な SEO 施策(3)(10月6日)
サーチからはじまるインタラクティブエージェンシー
サーチからはじまるインタラクティブエージェンシー
DB マーケティングと Web マーケティング 〜ビールとオムツの伝説から〜(10月6日)
最新ハイテク講座
最新ハイテク講座
視聴者が参加する時代へ!ネットにつながる「テレビ」(10月3日)
developer.com
developer.com
デザインパターンの使い方: Command(10月3日)
最新アフィリエイト事例にみる成功の法則
最新アフィリエイト事例にみる成功の法則
アフィリエイトメディアとの付き合い方(10月3日)
海外のインターネットコムアメリカ韓国ドイツトルコ
Copyright 2008 Jupitermedia Corporation All Rights Reserved.http://www.internet.com/