Monday, February 25, 2008

WebSphere から Oracle にアクセスすると、ArrayIndexOutOfBoundsException が発生した件

WAS から JDBC Thin ドライバを使って、Oracle 10g にアクセスしているのだが、表題のようなエラーが発生した。発生時の環境は以下のとおり。


  • Oracle JDBC ドライバのバージョン: 10.2.0.3.0
  • Oracle のバージョン:10.2.0 Express
  • WAS のバージョン:IBM WebSphere Application Server - Express, 6.0.0.1
  • WAS で使われる Java のバージョン:Classic VM (build 1.4.2, J2RE 1.4.2 IBM build cxia32142sr1w-20041028 (JIT enabled: jitc))


Oracle JDBC ドライバのバージョンは、java.sql.Driver のメソッドを呼び出さなくても、ojdbc14.jar を展開して MANIFEST.MF を見れば分かる。WAS のバージョンは管理画面から分かるし、WAS の java バージョンは、WAS 配下の java コマンドを -version オプションで実行すれば分かる。

この環境で以下のようなエラーが発生したというわけだ。

java.lang.ArrayIndexOutOfBoundsException: 451808508
at oracle.jdbc.driver.T4CCallableStatement.doOall8(T4CCallableStatement.java(Compiled Code))
at oracle.jdbc.driver.T4CCallableStatement.executeForRows(T4CCallableStatement.java:965)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java(Compiled Code))
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java(Compiled Code))
at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3445)
at oracle.jdbc.driver.OracleCallableStatement.execute(OracleCallableStatement.java:4394)
at com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.pmiExecute(WSJdbcPreparedStatement.java:632)
at com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.execute(WSJdbcPreparedStatement.java:427)
.....



ググるとこういうページが。

http://www.ibm.com/developerworks/forums/thread.jspa?messageID=14018143

どうも、IBM Java の JIT と JDBC ドライバの相性が悪いらしい。JIT を無効にする(環境変数 JAVA_COMPILER に NONE をセットする)ことで回避できるようだが、パフォーマンスに問題が出てくるかもしれないし、本番環境で同じ問題が出やしないかと心配なので、JDBC ドライバを本番環境で使っているもので置き換えて実行してみた。

ちなみに本番での環境は以下のとおり。本番では上の問題は出ていない。


  • Oracle JDBC ドライバのバージョン: 10.2.0.2.0
  • Oracle のバージョン:10.2.0 Enterprise Edition
  • WAS のバージョン:IBM WebSphere Application Server - Express, 6.0.2.17
  • WAS で使われる Java のバージョン:Classic VM (build 1.4.2, J2RE 1.4.2 IBM build cxia32142-20061124 (SR7) (JIT enabled: jitc)


上にあるとおり、本番環境では、Oracle JDBC ドライバのバージョンは、10.2.0.2.0 なのだが、これに置き換えてみると、表題のエラーは発生しなくなった。

Wednesday, January 16, 2008

Java 例外のコスト

気になったので次のようなコードで計測してみた。JDK のバージョンは 1.5 。


public static void main(String a[]) throws Exception {
int c = 0;
System.out.println(new Date());
for (int i = 0; i < 1000000000; i++) {
c = test(0);
}
System.out.println(new Date());
System.out.println(c);
}

static int test(int a) {
int c = 0;
if (a == 0) {
String s = new String("13");
return Integer.parseInt(s);
} else {
return c + 1;
}

}

ここでは、軽い仕事をして普通にリターンしている。


public static void main(String a[]) throws Exception {
int c = 0;
System.out.println(new Date());
for (int i = 0; i < 1000000000; i++) {
try {
c = test(0);
} catch (Exception e) {
c = i;
}

}
System.out.println(new Date());
System.out.println(c);
}

static int test(int a) throws Exception {
int c = 0;
if (a == 0) {
throw new Exception();
} else {
return c + 1;
}

}

ここでは、何もせずに例外をスローすることでリターンしているのだが、前のコードの 16 倍 の時間がかかった。Exception をその都度生成するのではなく、static に確保しておいて、それをスローするだけにすると時間はほぼ同じだった。

結局、例外の throw/catch そのものは大したことないが、例外オブジェクトの生成に相当のコストがかかることが分かる。この理由は、例外オブジェクトの生成時にスタックトレースのスナップショットをとることにある。これが高くつくというわけだ。(パフォーマンスへの目: 開発プロセスを改善する 中の例外のコスト)

したがって、パフォーマンスの面からだけでも、例外は例外状態のみに使用すべきであり、正常フローには使っていけないということになる。そして、正常フローでの使用を避けるために状態チェックメソッドや、null 等の区別できる値を返したりする。

ところで、例外がサポートされていない言語では、関数内でエラーが起こったときにそのエラーの種類を複数のエラーコードで返すようになっていることがよくある。

この場合、例外が使えるならば、回復可能と考えられるエラーにはチェックされる例外、そうでないエラーには実行時例外を対応させて例外クラスを定義し、エラーが起こったらそれをスローすることになるだろう。そうすれば、エラーコードと違って、呼び出し側に対応を強制することができ、コードの信頼性・保守性が向上する。これはまさに正しい例外の使い方といえる。

ただ、このようなメソッドでは例外状態であるにも関わらず、それが頻繁に発生するものがある。例外の生成には非常にコストがかかるので、これは困ったことであり、コードの信頼性・保守性とパフォーマンスとが両立しない状態になってしまっている。

このとき、例外を使わずにすまそうとするなら、状態チェックメソッドの導入が考えられるが、エラーが複数ある場合にはそれぞれに状態チェックメソッドが必要となり、メソッドが使いにくくなるし、成功か失敗かを判定するのにデータベースへのアクセスが必要だったりすると逆にパフォーマンスが悪くなってしまったりする。

これの解決案としては、例外のコストは例外オブジェクトの生成が大部分であり、throw/catch にはほとんどかからなかったことから、例外オブジェクトをあらかじめ static で作成しておき、それを使いまわすということが考えられる。

ただ、これをやると、スタックトレースの取得が不可能になるのと、(マルチスレッドで使用されるときには)エラー発生時の状況を例外オブジェクトに組み込むことができないという代償はある。

Friday, January 04, 2008

Linux を CD ブートして、ハードディスクの障害を復旧する方法

障害というほどではないが、実験中に重要な共有ライブラリ(libcrypt)の名前を変更してからルートを抜けてしまい、戻せなくなってしまった。sudosu もこのライブラリを使っているのでルートになることも新たにログインすることもできない。

そこで、インストールに使った Ubuntsu の CD からブートして復旧を試みた。当然、BIOS の設定はハードディスクより CD を優先してブートするようになっていなくてはならないが、幸い、これは問題なかった。

Ubuntsu のインストールCD でブートすると、最初にメニュー画面が出てきて Rescue Mode のような選択肢が選べるようになっている。とりあえずこれを選んでみたが、ファイル名を元に戻すだけなので、すぐに Alt-F2 のようにして別コンソールに移動して、シェルに抜けた。そこで


# mkdir /a
# mount -t ext3 /dev/sda1 /a


のようにして問題のディスクをマウントし、ファイル名を修正した。ちなみに /a は RAM ディスク上に作られている。

後は umount してリブート、CD をイジェクトして終わり。うまく元通りにできた。

Thursday, January 03, 2008

PostgreSQL 8.2.5 のインストール

PostgreSQL 8.1.5 をインストール と同様にソースからビルドしてインストール。実は以前インストールしていたのを忘れてて、再度インストールし直した。8.1.5 はシステムから削除した。

8.1.5 同様にインストールしたが、ライブラリまわりのトラブルは解決しておいたので、今回はスムーズにビルド&インストールがうまくいった。ただ、インストールやリグレッションテストのときにファイルの権限を PostgreSQL 用の管理ユーザに変更して実行するのは面倒なので、最初から PostgreSQL 用の管理者ユーザでビルド&インストールした。

つまり、postgres のホームに 8.2.5 のソースを展開し、

./configure --prefix=/opt/pgsql-8.2.5
make
make install

を実行。/opt/pgsql-8.2.5 はあらかじめルートで作成しておき、

# chown -R postgres:postgres pgsql-8.2.5

を実行しておいた。/opt/pgsql-8.2.5/ には /opt/pgsql というシンボリックリンクを張り、これを環境変数 PGDATA にセットするようにした。

リグレッションテストだが、インストールする前に最上位ディレクトリまたは src/test/regress/ ディレクトリで

gmake check

で実行できる。インストールしてから、src/test/regress/ 内で実行したとき、FAILED が結構出たのだが、make clean を実行してから再度実行するとうまくいった。どういう原因だったのかは分からない。

インストール後、管理者ユーザで

initdb -E UTF8

でデフォルトエンコーディング UTF8 でデータベースクラスタを作成し、

postgres

でデータベースサーバを起動できる。アーキテクチャが変更され、postmaster コマンドは廃止されている。サーバを起動すると、/src/test/regress/ で

gmake installcheck
gmake installcheck-parallel

とすれば、インストール後にリグレッションテストができる。パラレルチェックでは複数プロセスを起動して、テストスクリプトを並行に実行する。

Wednesday, January 02, 2008

java.util.List の size() メソッドをループの範囲チェックでは呼び出さない方がいい

List をループで使用する場合、次のようなコードを書きがちだ。

List li = new ArrayList();
// この辺は省略
for(int i = 0; i < li.size(); i++) {
Object o = li.get(i);
}

これは、次のように書いた方がいい。

List li = new ArrayList();
// この辺は省略
for(int i = 0, n = li.size(); i < n; i++) {
Object o = li.get(i);
}

僕のマシンでこのループを 10億回まわしてみたところ、ループの都度 size() を呼び出すと 43秒ほどかかり、初期化のところで1回だけ呼び出すようにすると 25秒ほどかかった。

修正後の所要時間は修正前の 58% にまで下がっている。

また、ここでは List の要素にアクセスするのにイテレータではなく、get(int) メソッドを使っている。これがふさわしいのは、List の実装がランダムアクセスの場合だけだ。LinkedList などの場合は、ランダムアクセスではなくシーケンシャルアクセスなので、イテレータを使うべきだ。

以上のことは、Joshua Bloch 著 『Effective Java』 の29項に触発されて調べてみた。

Saturday, October 13, 2007

ある関数従属性をもつ属性に対してどのようなリレーションを作成すべきか?

この投稿、近いうちに削除します。もう少し、うまくまとめて再度アップするつもり。

正規化の議論は、あるリレーションが正しい構造か?更新異常がない構造になっているか?という観点からのもの。ここでは逆に属性をどのように組み合わせていけば正しい構造のリレーションになるかを考えてみる。

まず、2つの属性 A, B は次の3つの関係(リレーションシップ)を持ちうる。

  • 1対1のリレーションシップ。このとき、A → B かつ B → A の関係である。
  • 多対1のリレーションシップ。このとき、A → B かつ B NOT → A の関係である。
  • 多対多のリレーションシップ。このとき、A NOT → B かつ B NOT → A の関係である。


1対1のリレーションをもつとき。

この場合、A と B は少なくとも1つのリレーション R 上に共存しなければならず、A または B が R のキーとなる。A が B を決め、B が A を決めるということは、A と B がそれぞれ何らかのエンティティ・インスタンスを決定していると捉えて、そのリレーションをつくるわけだ。そして、ある属性 C をこの R に追加するには、A → C または B → C が成立しなくてはならない。また、データの不要な重複を避けるために複数のリレーションに A, B が共存することは避けるべき。リレーション間のリレーションシップを作るために A または B が他のリレーションシップに存在する場合はあるが(いわゆる外部キーであろう)、その場合、あるリレーションには A、もう1つのリレーションには B というようにはせず、A または B の1つのみを使うようにした方がいい。

多対1のリレーションをもつとき。

この場合、A と B はリレーション内で共存でき、そのときは A がそのリレーションのキーになる。ある属性 C をそのリレーションに追加できるのは A → C が成立する場合だけ。

多対多のリレーションをもつとき。

この場合、A と B はリレーション内で共存でき、そのとき、(A,B) がそのリレーションのキーになる。(A,B) → C が成立するときだけ C をそのリレーションに追加できる。ただ、(A,B) NOT → C でも、C NOT → (A,B) だとすると、この関係は (A,B) と C との多対多のリレーションシップである。したがって、C をリレーションに追加して、(A,B,C) をキーとできる。ただし、こうしてしまうと、もはやこのリレーションは違う主題を表していることになるので、リレーションの名前を変えた方がよいということになる。

ちなみに、上で C → (A,B) が成立としたら、関数従属性の性質から C → A かつ C → B が成立する。この場合、多対1のリレーションということで、C,A,B が共存することは可能であり、C がそのリレーションのキーということになるだろう。

このように属性を組み合わせてリレーションを作っていけば、それらは DK/NF を満たすようになるだろう。

上の多対多のリレーションで (A,B,C) をキーにする場合だが、こうできるのは、多値従属性 A →→ B | C または B →→ A | C が成立しない場合に限られる。そうでないと 4NF にならず、当然 DK/NF も満たさないから更新時異常が起こってしまう。

たとえば、R{ 教授名, クラス } というリレーションがあって、教授とクラスの組み合わせがキーだとする。授業をする教室が教授の気まぐれで決まるとしたら、使用教室、という属性と (教授名, クラス) とは多対多のリレーションシップをもつ。そして明らかに、ここには多値従属性はないので、{ 教授名, クラス, 使用教室 } をキーとした新しいリレーションをつくることができる。

ドメイン/キー正規形(DK/NF)

この投稿、近いうちに削除します。もう少し、うまくまとめて再度アップするつもり。

リレーションの更新時異常を解消していく過程で、1NF, 2NF, 3NF, BCNF, 4NF, 5NF と正規化が進んでいく。5NF まで正規化されると更新時異常は起こらなくなるので、5NF がある意味究極の正規形である。

この正規形の系列とは別にドメイン/キー正規形(DK/NF)がある。この DK/NF でも更新時異常は起こらず、また更新時異常が起こらないリレーションは DK/NF であることが示されている。リレーションが DK/NF であるための条件は以下のとおり。

「リレーション上のすべての制約条件がキーとドメインの定義の論理的な帰結である場合にそのリレーションは DK/NF となる」

制約条件とは、「属性の静的な値を決定するためのルールで、真か偽かを明確に決定できるもの」である。したがって、関数従属性や多値従属性、リレーション間・リレーション内の制約条件などが該当する。なお、「静的な値」なので時間に依存する条件は含まない。たとえば、セールスマンの給料を表す属性値を変更するときは前の値より多くなければならない、などだ。

ドメインとは、属性の定義域のことだ。物理的定義と意味的定義の2つの側面があるが、DK/NF では物理的意味だけを考えればいい。

DK/NF の利点は、ドメインとキーというデータベース実務者にとって基本的な概念のみに関連していること。リレーションのすべての制約条件がこの基本的な概念の論理的な帰結になっているかどうかをチェックすればいいので、1NF から 5NF の正規化よりもずっと理解しやすくなっている。

直感的には、リレーションが1つの主題のみをもつようにすれば、DK/NF になるだろう。

Tuesday, October 09, 2007

多値従属性と 4NF

この投稿、近いうちに削除します。もう少し、うまくまとめて再度アップするつもり。


多値従属性(MVD)とは関数従属性(FD)を一般化した概念。したがって、FD は MVD でもあるということになる。

A, B, C, X をそれぞれ属性の集合としたとき、リレーション R{A,B,C,X} 上で、値 (A値,C値) に対応する B の値の集合が A値によってのみ決まり、C値とは独立しているとき、B は A に多値的に従属する、または A は B を多値的に決定する、という。これを A →→ B と書く。

「A の値によってのみ決まり C の値とは独立」とはどういうことかというと、ある A値をもつタプルの集合における B値、C値の集合を考えたとき、その「B値の集合」と、「C値の集合のそれぞれの C値に対応する B値の集合」が一致することをいう。このことから、A →→ B であるとき、かつそのときに限り A →→ C でもあることが分かる。そこで、この関係を A →→ B | C とも書く。

ここで X が空集合だとしてみる。すると R{A,B,C} はすべての属性の組み合わせをキーとする BCNF なるだろう。このとき、関数従属性と更新時異常で述べたように、R には FD を原因とする更新時異常は起きないが、A →→ B | C の MVD を原因とした更新時異常が発生する。

これを解消するために R{A,B,C} は2つのリレーション {A,B} と {A,C} に無損失分解することができる。結局、BCNF であるリレーション R をさらなる正規形に分解できたことになるのだが、この新しい正規形を第4正規形(4NF)と呼ぶわけだ。

4NF とは、「BCNF であり、かつそこにおけるすべての MVD が候補キーからの FD であるリレーション」と定義できる。

ちなみに無損失分解とは、分解してできた新たなリレーションを結合したときに元のリレーションに戻せるような分割のことだ。結局、正規化とは、更新時異常を避けるためにリレーションを無損失分解することだといえる。

Tuesday, October 02, 2007

1NF, 2NF, 3NF and BCNF

この投稿、近いうちに削除します。もう少し、うまくまとめて再度アップするつもり。

関数従属性に関する正規形の種類。関数従属性と更新時異常 で書いたように、BCNF までいくと関数従属性に起因する更新時異常がなくなる。じっさい、1NF, 2NF, 3NF は BCNF までの踏み台にすぎない。

BCNF の上に、多値従属性に関する正規形 4NF と結合従属性に関する正規形である 5NF がある。

まず、1NF。すべてのリレーションは 1NF になる。つまり、属性は単一の値(スカラ値)を持ち、同一のタプルは存在しないといったリレーションの定義を満たすものはすべて 1NF である。

2NF とは、キー以外のすべての属性がキー上で既約従属(キーの一部の属性に従属しない)であるリレーションをいう。これは候補キーが1つだけの場合の定義になるが、上で述べたように BCNF までの踏み台にすぎないのであまり形式的にやる必要もないだろう。

3NF とは、2NF であり、かつキー以外のすべての属性がキーに推移的でない従属をしているリレーションのこと。推移的な従属とは、A → B かつ B → C ならば A → C のような従属のことだ。このとき C は A に推移的に従属している。3NF とはこの推移的従属がないものをいう。ここでも候補キーが1つしかない場合の定義になっている。

BCNF とは決定項がすべて候補キーになっているリレーションのこと。つまり、R 上のすべての関数従属性の決定項が候補キーでもある場合、R は BCNF である。

Monday, October 01, 2007

関数従属性と更新時異常

この投稿、近いうちに削除します。もう少し、うまくまとめて再度アップするつもり。

リレーション R 上の属性で、A が決まると B も決まるとき A → B と書く。このとき A を決定項、B を従属項という。決定項、従属項は属性の組み合わせでもよく、A → {B,C}、{A,B} → C、{A,B} → {C,D,E} もある。

A → {B,C} と A → B かつ A → C は同じことである。が、{A,B} → C と A → C かつ B → C とは同じではない。

A → A および {A,B} → A は自明。従属性を考えるときは自明な従属性は除いていい。

すべての属性が従属するような決定項をキーという。キーが複数の属性から成り立つ場合、その一部がそれ以外の部分に依存することがあってはいけない(既約)。キーでない、またはキーの一部でない属性を非キー属性という。キーは複数存在することもあるので、候補キーともいう。

ここで、関数従属性と更新時異常との関係について考えてみる。

データベースというのは「モデルのモデル」すなわち、現実のビジネスに対するユーザの見方(ユーザのデータモデル)のモデルであった。したがって、リレーション上の関数従属性もユーザのデータモデルを反映している。

更新時異常とは、更新したときにユーザのデータモデルと不整合を起こす、または不整合を防ぐために何らかの異常な操作をしなくてはならないことだと定義できそう。

候補キーでタプルを識別し、更新・削除することは、結局ユーザデータモデル上で対応するインスタンスを更新・削除することに対応する。リレーション上で更新・削除されるのも1つのタプルだけであり、異常はない。

しかし、キーでない関数従属性の決定項でタプルを更新しようとすると、ユーザモデルでは更新されるべきインスタンスが1つに決まるはずだが(ユーザの頭の中にあるビジネスの見方に矛盾がない限りそうであるはず)、リレーション上では1つに決まらず複数のタプルを更新しなくてはならない場合がある。挿入しようとしても、決まらない属性があるのでタプルが決まらず、リレーションに挿入することができない。

したがって、このような更新時異常は、リレーション上の関数従属性の決定項がすべて候補キーになっていれば起こらない。こういうリレーションが、Boyce-Codd 正規形であり、ここまで正規化されると、関数従属性に起因する更新時異常は発生しなくなる。

Friday, September 28, 2007

vim で文字コードを指定するには?

たとえば次のように設定すればいい。

set encoding=utf-8
set fileencodings=utf-8,iso-2022-jp,euc-jp,cp932,latin1

encoding で vim 内部で使う文字コードを指定する。vim 内部とはバッファやレジスタ内の文字列、.viminfo ファイル内のテキストなどに使用されるという意味らしい。

fileencodings ではファイルを読み書きするときに考慮する文字コードのリストを指定する。ファイルを読み出すときに、このリストの順に文字コードを試していき、該当するものがあれば、それを使う。もし、その文字コードが encoding に指定しているものと異なる場合は文字コードの変換がなされるが、書き込むときは元のコードに再変換して書き込まれる。一致するものがリストにないなら encoding の文字コードで読み出される。

ファイル編集時に

set fileencoding?

とすると、ファイルの文字コードと判定された文字コード名が示される。上に書いたことから分かるように、これは fileencodings に指定したリスト内のいずれかになる。リスト内に一致したものがない場合は、この fileencoding は空になる。

fileencodings の設定で、latin1 (ISO_8859-1) を先頭に指定すると、UTF-8 や EUC-JP のファイルでも Latin1 と判定されてしまった。これは、それらのファイル内の少なくとも最初の部分の文字を構成するバイトが Latin1 でコードポイントを与えられているからだろう。

Windows の gvim の設定で(gvimrc ファイル) encoding に CP932 以外を指定すると、メニューなどの文字が文字化けした。冒頭で書いたように、テキストだけではなく vim 内部で使用する文字コードが設定されることが原因だろう。


参考サイト: vim の全オプション

vim の自動インデントで余分にインデントされてしまう件

vim でタブに対応する空白数はデフォルトで8のようだが、ホームディレクトリ直下の .vim ファイルに

set tabstop=4

と定義しておくことで、対応する空白数を4にセットできる。だが、こうすると C や Java のソースファイルを編集するときの自動インデントが余分に入るようになってしまった。つまり、1回インデントして空白が4つ入るはずなのに、2回インデントして8つの空白が入るようになってしまう。

この原因は、タブの空白数の設定と自動インデントによる空白数の設定とは別であるところにある。デフォルトでは自動インデントにおける空白数が8に設定されているようで、このためタブの空白数が4だと、8つの空白を入れるためにインデントが2回実行されてしまうというわけだ。


参考サイト: vim の全オプション


これを直すには、自動インデントの空白数をタブの空白数に合わせてやればいい。次のように設定する。

set shiftwidth=4

.vimrc に先の tabstop の設定と同様に定義しておけば、問題は解決する。

Monday, September 03, 2007

JDBC で SQL の LIKE 文を使うときの書き方

次のように書く。

String keyword = "富";
PreparedStatement st = con.prepareStatement("SELECT name FROM tbl_names WHERE name LIKE ?");
st.setString(1, "%" + keyword);

これにより、富、という文字で始まる名前が検索できる。つまり、LIKE 演算子に使う % や _ などのワイルドカードも含めた検索文字列を setString メソッドで、? にセットするということだ。

ではワイルドカードをエスケープするときは、どうするか?Oracle ではエスケープ文字は特に決まっておらず、ESCAPE オプションで指定する。たとえば、

SELECT id FROM tbl_names WHERE id LIKE '\%%' escape '\';

という SQL 文は、\ をエスケープ文字に指定しており、% という文字で始まる id を検索している。もし、\ がなかったなら、すべての id がヒットする。

JDBC の場合も同様にすればよい。

PreparedStatement st = con.prepareStatement("SELECT id FROM tbl_names WHERE id LIKE ? escape '\\' ");
st.setString(1, "\\%%");

こうすれば、% で始まる id を検索できる。念のため言っておくと、\\ のように二重になっているのは、Java の文字列中では \ はエスケープ文字であるため、二重にすることで \ そのものを表現している。

ただ、このエスケープ文字の指定は Oracle 固有であるので、違う RDBMS では通用しないかもしれない。そこで、JDBC はエスケープ構文が用意している。次のように書く。

PreparedStatement st = con.prepareStatement("SELECT id FROM tbl_names WHERE id LIKE ? escape {'\\'} ");
st.setString(1, "\\%%");

結局、Oracle との違いは、エスケープ文字を {} で囲っていることだけだ。


Oracle、PostgreSQL 等でこれを実験してみる。
そして
http://www.nilab.info/zurazure2/000740.html
このページも参考にしながら、続きを書く。

Monday, August 27, 2007

Windows に Active Perl, Apache, Tomcat のセットアップ

仕事でローカルマシンに HTTP サーバを立て、そこでテスト用 CGI を実行する必要があったためセットアップした。この CGI はリクエストを受けると、別のサーバにリクエストを送信し、結果を整形して返すということをする。この別のサーバにアクセスするためにはローカルマシン上で VPN クライアントを実行してトンネリングする必要があるため、この作業が必要になった。

Active Perl

ActiveState 社 のサイトからインストーラをダウンロードして実行すればいい。PATH の設定も自動でなされるので、DOS 窓から普通に実行できる。Cygwin を入れていれば Perl は元から入っているだろうが、当然、その Perl は Active Perl とは別のものだ。-v オプション付きで双方を実行すれば分かる。


Apache

http://www.y2sunlight.com/ground/?Apache2 を参考にセットアップ。インストーラをダウンロードして実行するだけでいい。個人的な実験用であり、サービスとして起動する必要はないなら、8080 ポートで手動実行という選択をすればいい。

スタートメニューの 『すべてのプログラム』 から Apache HTTP Server をたどっていき、Start Apache in Console をクリックすれば Apache が起動する。ブラウザで localhost:8080 にアクセスして起動を確認する。

CGI を動かすには、conf/httpd.confScriptAlias ディレクティブの設定が有効になっていなくてはならない。localhost:8080/cgi-bin/$APACHE_HOME/cgi-bin/ の中にある CGI スクリプトにアクセス、実行できるようになっているはず。

CGI が Perl で記述されているなら、冒頭にある Perl へのパスを次のように、インストールした ActivePerl にあわせて設定する。


#!c:/Perl/bin/perl.exe


テスト用 CGI もこのようにしてうまく動かすことができた。


Tomcat

Windows で Tomcat で 5.5 をインストール済み。CGI を有効にするには、少々追加設定が必要なようで、次のページを参考にさせてもらった。

http://muimi.com/j/jakarta/tomcat55/
http://www005.upp.so-net.ne.jp/burubon/epc/java-homepage/015.html

しかし、テスト用 CGI を実行することはできなかった。どうやら、CGI がサーバにリクエストを出すのに失敗しているようだった。Apache ではうまくいったから、Tomcat の問題なのだろう。

ちなみに、Tomcat インストール後に Java をアップグレードすると起動に失敗してしまうことがある。原因は、Tomcat に指定していた JVM のパスが変わってしまったことによる。Monitor Tomcat を起動し、それの Configure をクリックし、Java タブで JVM のパスを指定しなおせばいい。たとえば、次のように。


C:\Program Files\Java\jre1.5.0_11\bin\client\jvm.dll


これは JDK1.4 から 1.5 にアップグレードしたので、このように変更した。

Sunday, August 26, 2007

SonicMQ 7.0 インストール

Sonic の CD にある setup.sh を実行してインストール。root 権限は必要ない(むろん、インストールする場所による)。インストールに X-Window は必須。ライセンスキーを入力し、ほぼデフォルトのままインストールしてよい。

管理コンソールを使うこともできるが、$SONIC_MQ_HOME/bin/startcontainer.sh を実行すればすぐ使うことができる。screen で実行しておき、止めたくなったら Ctl-C で終了させてもいい。

SonicMQ を起動すると、デフォルトでは 2506 ポートをオープンしてクライアントを待つ。クライアントは、


progress.message.jclient.TopicConnectionFactory conFactory = new progress.message.jclient.TopicConnectionFactory();

conFactory.setConnectionURLs("<ホスト名 or IP アドレス>:2506");
conFactory.setFaultTolerant(true);

TopicConnection connection = conFactory.createTopicConnection();


のようにすれば TopicConnection を作成できる。TopicConnectionFactoryTopicConnection 同様に javax.jms パッケージで定義されているインターフェースだが、setConnectionURLssetFaultTolerant は Sonic 固有のメソッドだ。

冗長化構成をとることもできるが、これは調査中。

Tomcat5.5 で Web アプリケーションの配備

Web アプリケーションを配備するために、$CATALINA_HOME/webapps/ に war ファイル(ベース名はコンテクストルート名になる)をおくか、コンテクストルートそのものを置く。もしくは、$CATALINA_HOME/conf/server.xml に次のような XML を記述してもいい。


<Context path="/LoaderSide" docBase="/home/kazu/ContextCommunication/LoaderSide/build/war/loader.war"
debug="0" privileged="true" crossContext="true">
</Context>

<Context path="/StoreSide" docBase="/home/kazu/ContextCommunication/StoreSide/build/war/store.war"
debug="0" privileged="true" crossContext="false">
</Context>


ここでは、コンテクストルート名が LoaderSideStoreSide という2つの Web アプリケーションを配備している。LoaderSide は crossContext 属性を true に設定しており、他の Web アプリケーションのコンテクスト情報にアクセスすることができるようになっている(Web アプリケーションにおけるコンテクスト間通信)。

ここまでは、Tomcat4 と同じ。だが、Tomcat4 では可能だった、コンテクストフラグメント XML を $CATALINA_HOME/webapps/ に置く方法はうまくいかなくなった。

Tomcat5 では、コンテクストフラグメント XML ファイルのベース名をコンテクストルート名と同じにして(4 ではベース名は何でもよかった)、$CATALINA_HOME/conf/Catalina/localhost/ に置かないといけない。先の例だと、LoaderSide.xml と SoreSide.xml ファイルをそれぞれ作って、配備してやることになる。path 属性はあってもいいが、XML ファイルのベース名に指定したコンテクストルート名と食い違っているとまずいから、書かない方がいいだろう。

Sunday, July 29, 2007

RPM で screen をインストールするときに手間取った件

screen というのは、UNIX ライクな OS で動く仮想端末管理プログラム。SonicMQ をフォアグラウンドで起動してそのままにしておき、落としたいときに Ctl-C で終了させるということが仕事で必要になったので導入することにした。

まず、自宅の Ubuntsu に導入してみた。これは

# apt-get install screen

で一発。簡単に完了。

次に、本番環境となっているデータセンタの RedHat Linux へのインストールに取り掛かる。Web 上の RPM リポジトリ から screen-4.0.2-1.i686.rpm を取得して、rpm コマンドを下のように実行してインストールしたが、

# rpm -ivh screen-4.0.2-1.i686.rpm

これに対し、libelf と libtinfo というライブラリに依存しているというエラーメッセージが出た。Web 上の RPM リポジトリ でも依存しているパッケージがリストされており、そこにもこれらが表示されていた。そこから libelf-0.8.2-4.i686.rpm と ncurses-5.2-34.i686.rpm を取得して、rpm コマンドでインストールを試みた(libtinfo は ncurses パッケージに含まれるということだった)。

しかし、libelf の方はうまくいったが、ncurses の方は、もっと新しい ncurses パッケージがすでにインストールされているというエラーが出てしまった。

これには困った。問題は、なぜインストール済みの新しい ncurses パッケージには libtinfo が含まれていないのかということだ。先に screen をインストールした Ubuntsu マシンを調べてみると、libtinfo というライブラリは存在していない。

そこでもっと新しい screen のパッケージ screen-4.0.2-48.2.i586.rpm を取得して、

# rpm -ivh screen-4.0.2-48.2.i586.rpm

を実行すると、今度はうまくインストールできた。このパッケージは libtinfo にも libelf にも依存していないので、先にインストールした libelf も

# rpm -e libelf

で削除した。

おそらく古い screen では libtinfo ライブラリを使用しており、それが最新だった頃の ncurses パッケージにも libtinfo が含まれていたのだろう。しかし、新しい screen では libtinfo は必要なくなっており、ncurses パッケージにもそれは含まれなくなってしまった。そういうわけで、新しい ncurses パッケージがインストールしてあるマシンに古い screen をインストールしようとした結果、こういう問題がおきたということなのだろう。

Friday, June 08, 2007

Web アプリケーションにおけるコンテクスト間通信

crossContext 属性は、getContext() で取得する側を true に設定する。そうしないと、null が返る。

共通するクラスは WEB-INF/lib/ や WEB-INF/classes/ においては駄目。ClassCastException が発生する。Tomcat5 ではそういうクラスは、CATALINA_HOME/shared/lib/ や CATALINA_HOME/shared/classes/ に置けとある。Tomcat4 だと CATALINA_HOME/lib/ か CATALINA_HOME/classes/ に置けとあったがうまくいかなかった。見つけられずに NoClassDefFoundError が発生する。そこで、CATALINA_HOME/common/lib/ に置いたらうまくいった。

http://tomcat.apache.org/tomcat-4.0-doc/class-loader-howto.html
http://www.jajakarta.org/tomcat/tomcat5.0/ja/docs/tomcat-docs/class-loader-howto.html
http://www-06.ibm.com/jp/software/websphere/developer/j2ee/strategy/


http://www.fortunefield.com:8080/StoreSide/Store?data=fuckYou
http://www.fortunefield.com:8080/LoaderSide/Loader

$CATALINA_HOME/shared/lib/ を有効に使えるのは、$CATALINA_HOME/webapps/ に war ファイルを置いた場合のみのようだ。ただ、これだと crossContext 属性を設定できないので、コンテクスト間通信には使えない。。。

Thursday, June 07, 2007

Web アプリケーションのコンテクストパスを多重化する方法はあるか?

webapp1 という Web アプリケーションがあるとしよう。Tomcat ならば、CATALINA_HOME/webapps/ に webapp1.war というファイルがあるものとする(むろん、配備の仕方はこれ以外にもあるが)。

このとき、

http://www.fortune.com/webapp1/SomethingServlet

のようなパスでこの Web アプリにアクセスできる。これに /packman というコンテクストパスでもアクセスできるようにするには、CATALINA_HOME/webapps/ に次のような内容の XML フラグメントファイルをおけばいい。


<?xml version="1.0" encoding="EUC-JP" ?>
<!--
ファイル名は packman.xml でも hoge.xml でも何でもいい。
-->
<Context path="/packman" docBase="./webapp1.war"
debug="0" privileged="true">
</Context>


こうしてから、

http://www.fortune.com/packman/SomethingServlet

によるアクセスが成功する。ただし、この方法には重大な欠陥がある。これだと、webapp1 というアプリケーションが二重に起動されてしまうのだ。つまり、アプリケーションのインスタンスが2つできてしまう

一方の Web アプリから他方の Web アプリにリクエストを転送することで1つの Web アプリを共用化することはできる。ただ、いろいろ調べてみたが、Unix のシンボリックリンクのようにパスを多重化しつつ、ファイルの実体は1つだけにするというようなことはできないようだ。

検索をかけてみると、日本語のページはヒットしなかったが、英語では1つだけヒットした。

How to configure multiple context path for a webapps

Saturday, May 12, 2007

CPU 使用率 100% になって Windows がフリーズしてしまう件

タスクマネージャ で見ると、svchost.exe というプロセスが CPU リソースをバカ食いしている。これを思い切って終了させてみると正常に戻った。が、オーディオデバイスが使えなくなってしまう。

svchost.exe というのは重要なシステムプロセスだそうで、いくつかある同じ名前のプロセスを終了させるとシステムがシャットダウンしてしまう場合もある(他の svchost.exe を強制終了したら実際そうなった)。

そこで

http://gigazine.net/index.php?/news/comments/20061009_svchost/
http://www.microsoft.com/technet/sysinternals/utilities/ProcessExplorer.mspx

Process Explorer なるものをインストールして詳しく調べると、100% 近くなっている svchost.exe のプロセスツリーの下には、wuauclt.exe というのがある。これは、Windows の自動更新を司るプロセスだ。どうやら、これが原因らしい。

したがって、コントロールパネルから自動更新を OFF にすると問題は解決したが、この自動更新の不具合を修正するための更新プログラムが(いまごろ)発表されていたのでそれをインストールした。

http://gigazine.net/index.php?/news/comments/20070511_windows_update_patch/
http://support.microsoft.com/kb/937383/ja

これでこの問題は完全に解決。ただ、svchost が短い間 CPU 使用率100% になることはあるということで実際そうなっている。

この件、ググってみたら結構ヒットして、オーディオデバイスが使用不能になるなど、まさに同じ目にあっている人もいた。