2010年9月12日日曜日

ちょっとは暑さがマシになった夜のJava系小ネタ

Log4j、ログのローテートに失敗。原因はlog()メソッド?

プログラマとして社会に踏み出して5年目となる今年、ようやくLog4jを使用するシステムに巡り会えた(※1)。つっても、我々にはLoggerやAppender設計・製造する権限はなく、元請けが昨年製造・全社標準として採択されたjarを使わされるだけの立場なのだが。

言われた通りに組み込んで使用していると、不具合発生。ログファイルは1日ごとにローテーションする設定なのだが、日付が変わってもローテーションされず。さらに、出力済みのログファイルの中身を確認したところ、ある時刻までのログが全く出力されていない(その時刻以降は問題無く全て出力されているが)。

で、調べてみたら、以下の記事発見。java-jaの方でしょうかね。前述の通り日付ローテーションと言うことで、まさにこのAppender使っていた。原因はこいつか?別のAppenderに変更して試したところ…状況変わらず、暗礁空域へ…いや、待て。よくよく考えたら、標準ログ以外のログも出力する要件があり、いくつかのLoggerを自作していたのだが、自作の方はDailyだろうが何だろうが、ローテートに失敗したことは一度も無かった。ということで、不具合の原因はコンポーネント側にあると考えるのが順当だ。ということで、提供元に調査依頼を出した。

完全に我が社の責任範囲外の事象だが、そのコンポーネントのソースコードがjarと一緒に提供されていたので、ちょっと覗いてみた。…空のcatchブロックだらけじゃねぇか。例外殺しやがって、こりゃ発見遅れるわけだ、ったく…
正常にローテートされている自作Loggerと異なる部分を探してみる。すると、ログ出力するのに使用しているメソッドに違いが見られた。自作の方はinfo()とかdebug()とかログレベルの名称を冠したものを使用しているのだが、提供されたものはlog()メソッドを使用している。この違いが原因なのか?log()メソッドの部分をinfo()とかに替えて試してみたかったが、何かのクラスが足りなくてコンパイルできず、試行は断念…

Tomcat5.5にて、<jsp:useBean>タグでJasperException発生の原因

…Tomcatは5.0だったかもしれん…

過去最悪の仕事環境でのお話。JSP+サーブレットというアーキテクチャのシステム(Struts等のフレームワークは未使用)で、本番環境でのサーブレットコンテナは商用のあまり聞いた事のないやつ。そのサーブレットコンテナは、無償開発版といったものが無いので、皆サーバー環境でデバッグしていたのだが、リンク先記事にある通りコードがグダグダ。ステップ実行とかできないのは厳しすぎるので、ローカルにTomcat環境作ることにした。Javaのバージョンは我が社の案件にしては珍しく5.0だったので、Tomcatは5.5をチョイス。

環境構築して実行してみると…当節見出しの通りの不具合が発生。しかし、全ての<jsp:useBean>の箇所で発生するのではなく、決まったBeanでのみ発生している模様。これらBeanの共通点を探してみると…引数なしコンストラクタが定義されていないことが判明。

次に、JSPから変換されたJavaコードを確認してみた。<jsp:useBean>の箇所は、以下のロジックに変換されていた。
  1. 引数なしコンストラクタでBeanをインスタンス化
  2. setter・getterにて値の遣り取り
つまり、1.で未定義のコンストラクタが使用されて例外が飛んだっぽい。

JavaBeansの必要要件である「publicで引数なしのコンストラクタを実装」を行っていなかったために起こった悲劇であった。
コードレビューってやっぱ必要だよね。

正規表現メモ

範囲指定子の「-」。これ、括弧内([])の先頭か末尾に定義した場合、リテラルとして扱われる…ということを先月知りました。はぁ~…(自分に溜息)

*

※1
ちなみにJava5.0に巡り会うのには4年かかった。
「人月計算とExcelとスーツの世界」はホント石橋を叩きすぎだ(それとも我が社の案件が極端なのか?)。

2010年9月7日火曜日

暴風前夜のeclipse

「.metadata」は定期的にバックアップしとけ

eclipseを長く使っていると、ある日突然、起動できない、ビルドできない等のトラブルが時々発生する。原因の殆どは、ワークスペースフォルダ直下でeclipseを設定情報を管理している「.metadata」の破損。トラブった際は.metadata内のあのファイルをああしろこうしろと言った情報がWebに溢れているが、定期的に丸ごとバックアップしておいてズドンと上書くのが一番手っ取り早いです(当然、バックアップ取得以降の変更内容は失われますが)。

SVN管理化のJavaソースファイル名称変更後に発生した、同期化エラートラブルの記録

一般的なトラブルかどうか分からんが、一応記録しておく。

SVN管理化のJavaクラスの名称を、リファクタリング機能にて変更した。Javaの場合、クラス名=ファイル名という決まりなので、必然的にファイル名も変更することになる。これにより、変更前のJavaファイルは「削除」、変更後は「新規」扱いとなり、それぞれコミットした。すると、それ以後、ファイル名変更をコミットしたメンバー(厳密にはworkspace?)以外の開発環境において、同期化エラーが発生するようになってしまった。

上記のトラブルは度々発生した。何が原因なのか結局わからなかったが、トラブル発生直前の操作内容を見聞すると、以下の共通点が浮かび上がってきた。
  1. クラス名を変更した。
  2. 変更するのにeclipseのリファクタリング機能を用いた。
  3. 新規ファイル(クラス名変更後のJavaファイル)は、「バージョン管理に追加」せずに、いきなりコミットした。
これらを踏まえて、クラス名変更時の手順を以下のようにしたら、トラブルは発生しなくなった。
  1. クラス名を変更する際は、リファクタリング機能は使わない。
    つまり、新名称のJavaファイルは新規作成、旧名称のは普通に削除。
  2. 新名称Javaファイルは、まず「バージョン管理に追加」し、それからコミットする。
上記解決方法は別の人が調べて持ってきてくれたのだが、1・2どちらか片方だけではダメだったのだろうか。まあ「ダメだった」って言ってたからダメなのだろうが…一度自分で試そうとしたが、これ以上トラブル発生して作業が停滞するのもしんどかったので(てかプチ炎上中だったし)、実行に映さず。

ちなみに、そのリポジトリのルートディレクトリに対するアクセス権限が与えられていなかったのだが、関係あるのかな?

別リポジトリ管理化のリソースをコピペしてくる際の注意事項

ユーティリティ群など汎用的な部品は、別プロジェクトで作成したものをコピーしてくるという事はよくやると思うが(※1)、その際は「.svn」はコピーしないこと。どのリポジトリに接続するかという情報はこの中で管理しているので、「.svn」ごとコピーしてしまうと、同期化時に別のリポジトリに対する接続ダイアログが何度も表示されたり、「遮断」的なエラーが発生したりする。

ちなみに、TortoiseSVNのエクスポート機能を利用すれば簡単に削除できる。

※1
てかjar化しろって話か。

2010年9月5日日曜日

昨日よりはマシな夜のOracle

長さ0の文字列('')はNULLとして扱われる

「フィールドの値がスペースのみのデータを抽出」という仕様を実現するのに「TRIM( [フィールド名] ) = ''」と組んだが、期待した結果とならず。TRIM( [フィールド名] )をSELECTして戻り値を見てみると、値がスペースのみの場合は何とNULLが返ってきている。あれっ!?空文字とNULLは別物の筈だが…何とも不可解であるが、ゴーイングマイウェイなOracleならあり得るかと、素直に抽出条件を「TRIM( [フィールド名] ) IS NULL」と修正した。

後で調べてみたら、やはりOracleの独自仕様らしい。DB界の泰斗であらせられるミックさんの記事で知った。 上記記事によると、これに関してはOracleも反省しているのか知らんが、将来修正されるかもしれないとのこと。マジかよ。そうなったら上記みたいなSQLは修正しなきゃいけないじゃないか…

ANSI SQLの外部結合構文(OUTER JOIN)を使用時のバグが盛りだくさん

バージョン9i,10gにて、SQLの結合構文にANSI SQLのOUTER JOINを使用すると、エラーや結果不正が発生することがあるらしい。まあ、結構有名な話ですが。

ちなみに、具体的なバグの発生条件・内容や修正パッチは、サポート契約しないと入手できない(ケッ!)。
ネットに転がってないか散策していたら、以下のページを発見。何かのグループウェアのページのようだ。
上記ページの「Oracle9i,10gにおける不具合事項」の節に
「問題を修正した9.2.0.8,10.1.0.5,10.2.0.2のPSRが提供されています」
とあるが…本家発の情報じゃないので、何とも言えないな…

余程複雑なSQLでなければ、まあ大丈夫だというような意見をどこかで聞いたが…発生条件が不明である以上、あの忌まわしき(+)を使わざるを得ない。はぁ~…

複合主キーにまつわる幻?

ここで今から述べることは、夢幻の可能性大。

Oracleのバージョンは9i、複合主キーを使用したテーブルに、キーの一部がNULLのレコードを登録できてしまった…気がする。
例えば、複合主キーが3項目からなる場合、以下のようなレコードが登録できてしまった…気がする。
INSERT INTO table VALUES ('pk1', 'pk2', NULL, …)

しかも、複合主キーの一部がNULLのレコードは、幾つでも登録できてしまった…気がする。NULLはどの値とも等しくはならない(例え比較対象がNULLであっても)からだろう。

しかし、ネットで調べてみると、「複合主キーの一部にはNULLをセットできる」なんて誰も言ってない。自宅PCのMySqlで試したら、見事にはじかれたし。Oracleで試せって話だが、重たいし汚れるから自宅PCに入れたくない。

うーむ、俺、疲れてたのかな…