雑記帳 2009年 5月第5週

2009/05/24 Sun.

おいおい、「携帯動画変換君」は思っていた以上に凄いな。mp4からmp3の抽出とかもできるのか。
これで「YouTube」からmp4で落としたモノの音声だけも引っこ抜けるわけか。想像以上に使い道が広そうだわ。

2009/05/25 Mon.

今日もCommons Loggingとlog4jを色々と。そもそも、Commons Loggingはどういう順序でログ出力方法を確定させてんだ?
ってなワケで、色々なバリエーションを作って試してみた。解説サイトもあるけど、自分で確認しないと覚えられないしね。

if (クラスパスの通った場所にcommons-logging.propertiesが有る) {
    if (commons-logging.propertiesのorg.apache.commons.logging.Logに
            org.apache.commons.logging.impl.Log4JLoggerが指定されている) {
        if (クラスパスの通った場所にlog4j.xmlが有る) {
            ★log4j.xmlの設定にしたがって、Log4jでログ出力
        } else if (クラスパスの通った場所にlog4j.xmlが無い) {
            if (クラスパスの通った場所にlog4j.propertiesが有る) {
                ★log4j.propertiesの設定にしたがって、Log4jでログ出力
            } else if (クラスパスの通った場所にlog4j.propertiesが無い) {
                log4jが警告を出す(処理自体は続行されるが、ログは出ない)
            }
        }
    } else if (commons-logging.propertiesのorg.apache.commons.logging.Logに
            org.apache.commons.logging.impl.Jdk14Loggerが指定されている) {
        if (VM引数に-Dオプションでjava.util.logging.config.fileが指定されている) {
            if (java.util.logging.config.fileで指定したファイルが有る) {
                ★java.util.logging.config.fileで指定したファイルの設定に従って、JDK標準APIでログ出力
            } else if (java.util.logging.config.fileで指定したファイルが無い) {
                ログは出力されない
            }
        } else if (VM引数に-Dオプションでjava.util.logging.config.fileが指定されていない) {
            if (JREのlibにlogging.propertiesが有る) {
                ★JREのlibのlogging.propertiesの設定に従って、JDK標準APIでログ出力
            } else if (JREのlibにlogging.propertiesが無い) {
                ログは出力されない
            }
        }
    } else if (commons-logging.propertiesのorg.apache.commons.logging.Logに
            異常な値が指定されている) {
        LogFactory.getLogを呼んだ時点で例外(LogConfigurationException)が発生する
    }
} else if (クラスパスの通った場所にcommons-logging.propertiesが無い、
        またはcommons-logging.propertiesのorg.apache.commons.logging.Logの設定値が無い) {
    if (クラスパスにLog4jのライブラリが有る) {
        if (クラスパスの通った場所にlog4j.xmlが有る) {
            ★log4j.xmlの設定にしたがって、Log4jでログ出力
        } else if (クラスパスの通った場所にlog4j.xmlが無い) {
            if (クラスパスの通った場所にlog4j.propertiesが有る) {
                ★log4j.propertiesの設定にしたがって、Log4jでログ出力
            } else if (クラスパスの通った場所にlog4j.propertiesが無い) {
                log4jが警告を出す(処理自体は続行されるが、ログは出ない)
            }
        }
    } else if (クラスパスにLog4jのライブラリが無い) {
        if (VM引数に-Dオプションでjava.util.logging.config.fileが指定されている) {
            if (java.util.logging.config.fileで指定したファイルが有る) {
                ★java.util.logging.config.fileで指定したファイルの設定に従って、JDK標準APIでログ出力
            } else if (java.util.logging.config.fileで指定したファイルが無い) {
                ログは出力されない
            }
        } else if (VM引数に-Dオプションでjava.util.logging.config.fileが指定されていない) {
            if (JREのlibにlogging.propertiesが有る) {
                ★JREのlibのlogging.propertiesの設定に従って、JDK標準APIでログ出力
            } else if (JREのlibにlogging.propertiesが無い) {
                ログは出力されない
            }
        }
    }
}

確認してみたら、一応こんな感じになった。本当は「Avalon」とかもあるんだろうけど、聞いたことも使ったことも無いので省略。
ちなみに環境はJDK1.5.0_11とcommons-logging-1.1.1.jar、log4j-1.2.15.jarを使いました。IDEはEclipse 3.2です。
これで何とかCommons LoggingとLog4j、JDK標準APIによるログ出力がある程度は理解できた。良しとしよう。

Commons Logging Log4j JDK標準API
fatal fatal SEVERE
error error SEVERE
warn warn WARNING
info info INFO, CONFIG
debug debug FINE, FINER
trace trace FINEST

ログってのは思ったより奥が深そうだなー、しかも今後はslf4jとLogbackを覚えていかないといけなさそうだしさ。

あーそうそう、「先週の雑記」の内容は間違いだった。「DatabaseConnection」じゃなくて「MySqlConnection」でした。
で、slf4jです。DBUnitで使ってるみたいなので、しょうがなく落としてきました。バージョンは1.5.6ってヤツです。
解凍してみたらjarが大量にあるのよ。一体どれを使えばいいんだ?とりあえずメインっぽいslf4j-api-1.5.6.jarを使用。
TestCaseからオーバーライドしたsetUp()でDBUnitによるバックアップとデータ投入をして、JUnitの試験用メソッドを実行する。
すると、org.slf4j.impl.StaticLoggerBinderのロードに失敗とかいう警告が出てきた。使ったjarが間違ってるのかな。
ロードに失敗したクラスはslf4j-simple-1.5.6.jarにあったので、slf4j-api-1.5.6.jarと差し替えてみて、再度実行してみる。
今度はNoClassDefFoundErrorが出て、org.slf4j.LoggerFactoryが無いと怒られた。これはslf4j-api-1.5.6.jarにあったし。
二つとも使用した上で、再度実行。すると、今度はNoSuchMethodErrorが出てきた。ちょっと待て、ンなアホな。

org.apache.poi.hssf.usermodel.HSSFRow.createCell(I)Lorg.apache.poi.hssf.usermodel.HSSFCell

うーん、POIのバージョンがマズい?確認してみると、poi-3.0-rc4-20070503.jarだった。何でこんな古いの入れたんだろ。
最新版を落としてきたら、poi-3.2-FINAL-20081019.jarだったので差し替えてみる。再度実行……やっと動いた!
試験用メソッドは空だけど、何もしないメソッドを二つ書いて実行したら、ちゃんとsetUp()tearDown()も二回ずつ呼ばれました。
DBUnitを使ったJUnitの単体試験ってのは、こうやって作っていくモンなのね。やっぱり自分でやらないと理解できないわ。

で、setUp()tearDown()でDB全体のバックアップとリストアってのはどうなんだろ。ここでやる必要ってあるのかな?
日常的に使うDBと試験用DBが同じなら意味ありそうだけど、試験用DBが独立してればバックアップやリストアは要らんかな。
試験用メソッドが動くたびにバックアップ取ったり戻したりじゃ、DBサーバへの影響が結構大きそうな気もするし。
……試験用メソッド内で試験対象となるテーブルに対してのみ、バックアップを取って戻して、とした方がいいんだろうね。
そうすると、setUp()tearDown()はDB接続・切断の制御だけになるのかな。いや、本当にこれでいいんだろうか?
試験が動く前後にsetUp()tearDown()は動くけど、試験クラス自体の開始・終了時に動くsetUp()tearDown()って無いのかな。
setUp()で初回だけDB接続するとかはできるけど、最後の試験メソッドであるかどうかはtearDown()からじゃわからないだろうし。
どうすりゃいいのかなーと調べていると、「DBUnitを利用した単体テストに関して」というページが。なるほど、勉強になります。
検証後にトランザクションをロールバックすれば、そもそもバックアップを取ることすらも要らんということか。
明日にまた試行錯誤してみよう。バックアップファイルをFile#delete()で消せないなど意味不明の現象もあるけど。

2009/05/26 Tue.

懲りずにDBUnitの続き。ってか、Excelの情報を登録するくらいにしかDBUnitを使ってなくね?もっと覚えることありそうだが。
で、試験対象としたDB操作クラスの設計をちょっと変えて、一応はdeleteとinsertとupdateのテストケースを作ることができた。
JUnitは4.6なので、@BeforeClassでDB接続、@AfterClassでDB切断、@Before@Afterはロールバックのみで書いてみた。
@TestではExcelのデータをクリーンインサートしてinsertやらを試験対象クラスで行い、DBの結果を結果予測のExcelと突合。
本当にこんな方法で、JUnitやらDBUnitの使い方は合ってんだろうな?自分でも思うけど、適当にも程がある気がする。

試験対象クラスのinsertやらを行うメソッドは更新件数を返すけど、そういやassertEquals()を使っていなかった。
DBUnitを使ったこの試験クラスはTestCaseを継承して作っていないんで、そのままじゃassertEquals()は使えないよな。
この試験クラスにextends TestCaseを付けても動くのか?わからん事はとりあえず何でもいいから試してみるに限る。
早速試したら、コネクションを触るsetUp()がいきなり動いて、全テストケースがNullPointerExceptionで落ちました。
あれ、@BeforeClassってJUnitの機能じゃないんすか?何でいきなりsetUp()から動き始めるんだろう。理解してない証拠だ。
Eclipse 3.2からJUnit 4系に対応していて、TestCaseを継承してクラスを作る必要は無いが、assertEquals()が使えないじゃん。
よく考えたらassertEquals()ってstaticメソッドか。import staticorg.junit.Assert.assertEqualsを書いたら使えました。
このimportjunit.framework.Assert.assertEqualsに変えても動いた。どっちが正?前者の方が機能が豊富そうだけど。
つーか、JUnitやらDBUnitを使う前に、機能と構成をある程度は把握しないとダメだよな。try & errorじゃ胡散臭すぎる。

で、更新系の確認はわかったけど、肝心の参照系の確認の仕方がわからない。一体どうしたらいいモンなんだろうか。
試験対象クラスで参照結果をObject[][]で返すメソッドがあるけど、これってDBUnitとかで確認する方法あるの?
一応は試験用データとselectの結果をExcelに書いてみたけど、実際に取ってきたデータとの突き合せ方法が不明。
IDataConnection」のcreateQueryTable()だと、SQL自体の動きが正常かどうかの確認はできると思う。
でも、「PreparedStatement」を引数にObject[][]を返すようなメソッドを試験する場合はどうしたらいいんだ。
そもそもDB操作を一つのクラスに突っ込んでいるのが間違いなのか?本来はバッチ用に設計したクラスだからなー。
DBに触れる箇所をあちこちに点在させたくないんでこんな設計になったんだけど、こういう設計思想は間違ってたりする?
まあクラス設計的にDBUnitでの試験が可能なものと不可能なものがあるんだろう。とりあえずDBUnitはこれくらいにするか。

あと、忘れないようにメモ。log4j.xmlでルートロガーというかデフォルトロガーのレベル指定の方法がわからなかった。
propertiesファイルとxmlファイルでの項目の書き方をマッピングしたようなサイトとか見つからないものだろうか。

<root>
    <level value="info">
    <appender-ref ref="file">
    <appender-ref ref="stdout">
</root>

@IT:Spring Frameworkで理解するDI(1)」ってのを見かけたので、ちょっと触ってみようかなとか思ったり。
まずはライブラリを落としてくるんだけど、APIドキュメントとかも入ってる一式で50MB近いとか、結構な量があるのね。
解凍したらjarの数が尋常じゃない。「こっちのページ」だとspring.jarとしか書いてないし。まさか全部は要らないよな?
で、要するにxmlで定義した情報をデータクラスに突っ込めるってこと?前述の例を参考にソースを書いて実行してみる。
不要なjarは入れたくないので、ソース書いてから必要となったクラスをjar tvfで探してjarを入れて、を繰り返す。
……動かすとFileNotFoundExceptionが出る。指定したxmlが無いって言われてるんだけど、100%あるんですが。
new java.io.File(filePath).exist()でもtrueが返ってくるんだけどな。スタートで物凄い躓き方してるんですけど。
ClassPathXmlApplicationContextのコンストラクタのパスが間違ってるとは思えない。一体何でファイルが見えないんだ?
xmlファイルの置き場はソースと同じ場所。参照パスで指定しても絶対パスで指定しても見えないと怒られる。何故だあー。

2009/05/27 Wed.

今日も懲りずにSpringをやりますよ。何かもう一々ハイパーリンク貼るのがメンドくなってきた。キーワード多すぎるわ。

で、引っかかっていたFileNotFoundExceptionが直らない。Eclipseをやめて、適当なトコにソースとxmlとライブラリを置く。
パッケージとかは全部無しにして、とりあえず本当に最低限のコードだけにして、手動でjavacを叩いてから実行してみる。
……おお?FileNotFoundExceptionでは落ちなくなったが、どうやらまだ不足ライブラリがあるらしいので適宜追加する。
最終的にはCommons-LoggingとSpringのasm, beans, context, core, expressionのライブラリを使うことになった。
さて、再度実行――今度はorg.antlr.runtime.Lexerが無いと怒られた。あれ、Springのライブラリとは無関係じゃね?
Antlr - Wikipedia」によると、Eclipseのプラグインとしても存在するライブラリらしい。「ここ」から3.1.3のライブラリを入手。
で、先ほど存在しないと怒られたクラスが対象のライブラリに存在するのを確認して、次こそはと再度実行してみる。
……えーっと、今度はNoSuchMethodErrorですか?何つーか、もはや手がつけられないんですが。どうしろっちゅーの。
@IT:Spring Frameworkで理解するDI(1)」によると、そもそもライブラリはcommons-loggingとspringしか書いてないんだよな。
何となくだけど、前提からして間違ってる予感がする。何つーか、そもそも使ってるSpringのライブラリは正しいモノなのか?
SpringSource」のDownload Centerでは3.0.0.M3が最新だけど、「Spring Framework - Wikipedia」は2.5.6.SEC01になってる。
やっぱり落としたモンが間違ってんのかな。しょうがないので、2.5.6SEC01の方を落としてから解凍してみる。
また大量のjarが出てきてイヤになっちゃうけど、恐らく目的のブツであろうspring.jarを発見。これが欲しかったんだよ!
さっき落とした「Antlr」も消して、spring.jarとcommons-logging-1.1.1.jarのみを使って、手動でコマンドを叩いてみる。

C:\java_src>javac -cp .;./spring.jar;./commons-logging-1.1.1.jar SpringTest.java
C:\java_src>java -cp .;./spring.jar;./commons-logging-1.1.1.jar SpringTest

XMLもライブラリもソースも、全て同一ディレクトリに配置。試してみたところ、やーっと動いてくれたよ。長かったわー。
一体俺が費やした時間は何だったんだ。落としてきたライブラリが違っていたとか、いくらなんでもアホにも程がある。

よし、早速Eclipseの方のライブラリも差し替えて、同じようなソースで実行してみる――来た!FileNotFoundExceptionが!
そういやXMLのパスを指定しても参照できない問題が解決してなかったじゃん。何となく予想は付いてきたんだけどね。
手動でjavaコマンドを叩いた際、-cpでカレントディレクトリも指定していたし、恐らくはクラスパスの問題なんだろう。
Eclipseのプロジェクトのディレクトリ直下にconf_springというディレクトリを作って、とりあえずそこにXMLを放り込んでおく。
んで、プロジェクトのプロパティからクラス・フォルダの追加でconf_springを指定しておく。恐らくこれで動くだろう。
早速実行してみると、またFileNotFoundExceptionが出やがる。うーん、ファイル名を指定する部分がマズいんだろうな。
プロジェクト直下が起動時のカレントになっているため、「./conf_spring/config.xml」という指定の仕方をしてました。
そもそもクラスパスにあるなら、ファイル名だけを直で指定すればいいんじゃないか?またもや直してから再実行。
すると、今度はClassNotFoundExceptionだと?一応はXMLの参照は正しくできた模様。で、クラスが無いってのは何事だ。
トレースを読むと、XMLの情報でデータ注入する対象クラスが無いと怒られてる。ああ、XMLに書くクラス名は完全修飾名か。
パッケージも含めたクラス名に直してから再実行。おおお、やーっと動いてくれた。どんだけ時間かけてんだよ。
結局「ClassPathXmlApplicationContext」のコンストラクタで時間食っただけ。まさかXML読むのに手間取るとは思わなかった。
いやさ、このAPIドキュメントのコンストラクタの部分に、「XMLはクラスパス上に置け」なんて書いてないじゃんかー。
腑に落ちないまま「@IT:Spring Frameworkで理解するDI(1)」を読み進めると、何かもうイヤになっちゃうような記述があった。

クラスが作成できたら、以下のapplicationContext.xmlというファイルを作成します。
このapplicationContext.xmlはアプリケーションのクラスパス上に配置してください。

あーもう、これから何かの解説を読みながら作る際には、一回全部の解説を読んでから始めることにしよう。
とりあえず先に進めるってことで、色々サンプルを試しながら確認完了。こういうDIは何の用途に使うのがいいんだろう。
お遊びアプリとかなら、データクラスを使って試験する際に想定通りの値で手軽に初期化できるのはいいね。
でも、XMLによるデータ注入自体は他のフレームワークでも提供されてるし。最近触ったのだとSeasarとかかな。
そういや、肝心のSpringの機能は何も触ってない気がする。AOPは手軽な環境で簡単に実装できるようなモノなのだろうか。

今度は「Spring Framework - TECHSCORE - 」のサンプルを動かしまくってみる。サイトによって説明は全然違うから面白い。
どうやらSpringのDIは単にフィールドにデータ突っ込むだけではなく、パラメータ有りのコンストラクタも呼べるみたいだ。
試しに、手持ちのDB操作クラスのコンストラクタにurl, user, passwordを渡すとDB繋ぐように、少し設計を変えてみる。
で、xmlでconstructor-argを使ってインスタンスを生成してみた。おおー、すげー、接続状態でインスタンスが出来てる。
接続情報に変な値を入れると、接続失敗時にコンストラクタでExceptionを投げるため、Springがそれをラップして落ちるみたい。
ここ」によると、SpringによってXMLから生成されるインスタンスは、デフォルト設定ではSingletonになるらしい。
すなわち、同一のBeanのidから複数のインスタンスを生成しても、それらを==で比較するとtrueが返ってくるワケだな。
あまり細かい部分までは知らなかったので、Singletonについても少しばかり勉強。意外に面白い情報で役立ちそうだ。
合わせてsynchronizedも真面目に覚えようと思ったけど、ここから先は明日だ。どうせ一日で全部は頭に入らんだろうし。
いわゆるstaticメソッドでのgetInstance()みたいな使い方とかにも関わるだろうし、ここらでしっかりやっておくか。

2009/05/28 Thu.

昨日の復習をしつつ、interfaceの利用によるXMLでの機能切り替えを試してみた。それにしても上手く作られてるモンだな。
再コンパイル無しで、使用するinterfaceの実装クラスを切り替えられるのは面白い。使う機会があるかどうかは不明。

恥ずかしながら、自分はabstractの理解が中途半端だったりする。JUnitやら何やらで普通に使ってはいるんだけどね。
この際なので、ちょっとばかり真面目にやっておくことにする。つーか、7年目にしてこの理解度ってのはマズいだろ。
ついでにinterfaceの意義についても再確認。定数interfaceって、やっぱり作るべきではないんだろうなあ。
前にいたPJでは定数があちこちに散在してて、それらは全部interfaceで定義されているという実装手法になってました。
テーブルA用定数はTableAConst、テーブルB用定数はTableBConstみたいになってて、全部interfaceになってた記憶が。
ロジック部分はTableAもTableBも見るので、定数interfaceを両方ともimplementsして使う必要があった。
そもそも抽象メソッドの無いinterfaceってのは、interfaceの意義的にどうなのかね?邪道だったりするのかな。
interfaceはメソッドの書式のみを集めた設計書みたいなモノだろうし、定数interfaceの存在意義は微妙かもしれん。
素直に個別にクラス化した上で、import staticで使っておけってことなのかな。その方が正しそうな気はするけど。

オブジェクト指向プログラミングへの道 1日目:オブジェクト指向でないプログラム : 富士通」ってページを発見。
一番最初に書かれているサンプル、これの出力結果が間違ってね?4000円、4220円、7000円じゃないのか?
試しにサンプルソースを丸写ししてみたら、4000円、4220円、7000円って結果になった。出力サンプルが間違いじゃねえか。
この後に続くオブジェクト指向を反映したソースだけど、Planクラスに通話時間を持たせるのって正しいのか?

2009/05/29 Fri.

Eclipse 3.4が重くて使い物にならんので、自宅のマシンにもEclipse 3.2を入れることに。起動待ち時間が長くてイラつくわ。
Eclipse 3.2の本体は「ここ」から、言語パックは「ここ」から、ついでにJDK1.5の日本語版JavaDocは「ここ」から落としておく。
参考サイトを見ながら、「Eclipse 3.2を日本語化」して、「JavaDocを関連付け」して、「MergeDocでホバーも日本語化」する。
……あれ、最後までやったのに、何故かクラス名の上にカーソル乗せてもホバーが出てきてくれない。これは困った。
Eclipse 3.4だとクラスにフォーカス当ててF2を押せばクラス説明のホバーは出せたけど、3.2ではF2じゃ出てくれない。
F3を押せば日本語化したソースにジャンプしてくれるし、Shift+F2だとJavaDocも開いてくれる。でも、ホバーだけが出ない。

あ、今日は職場の先輩の送別会でした。直属の先輩がいなくなると、俺は誰に勤怠連絡をすればいいんだろうか。
来週には次の仕事の面接が入ってるけど、また遠いトコなんだよな。何だよ、面接場所が秋葉原より徒歩15分って。
しかも作業するとなれば、その秋葉原より徒歩15分のトコか、新橋か門前仲町になる。ホントいい加減にしてくれ。

2009/05/30 Sat.

undefined.