Tuesday, November 14, 2006

Ubuntsu Linux でのロケール設定

.bash_profile で

LC_ALL=ja_JP.eucJP

というような 設定にしていたのだが、たとえば perl を起動したときに

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = "ja_JP:ja:en_GB:en",
LC_ALL = "ja_JP.eucJP",
LC_MESSAGES = "C",
LC_CTYPE = "ja_JP.eucJP",
LANG = "ja_JP.eucJP"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").

のような Warning が発生していた。svn コマンドを実行したときも似たようなメッセージが出た。

これは、/usr/lib/locale/ にロケールの定義がないことが原因らしい。デフォルトでは

en_US.utf8/
ja_JP.utf8/

という2つがあり、この中でそれぞれのロケールが定義されている。そこで、/etc/locale.gen ファイルに

ja_JP.eucJP EUC-JP

というエントリを定義して、ルート権限で locale-gen コマンドを実行した。これにより新しく ja_JP.eucjp/ ディレクトリが作成され、先ほどの Warning メッセージは出なくなった。

ただ、最初のうち、local.gen ファイルを編集し、locale-gen コマンドを実行しても ja_JP.eucjp/ は作成されなかった。locale-gen コマンドに直接 ja_JP.eucjp を指定したりして、いろいろ試しているうちに成功した。原因はよく分からない。。。

Saturday, November 11, 2006

Apache2 でアクセス認証を設定したときのディレクトリインデックス表示の変化について

マイサーバに Apache2.0 をセットアップした。Ubuntsu の apt-get でパッケージをインストールしたら、デフォルトで

/~kazu/public_html/

へのアクセスができた。~kazu/public_html/ には DirectoryIndex ディレクティブに指定してある名前のファイルがないのでディレクトリ内がインデックス表示されるようになている。

このディレクトリには、private_html という名前の ~kazu/ へのシンボリックリンクがある。つまり、このリンクをたどれば自分のホームディレクトリへアクセスできるようになっている。

自分のホームは非公開にしたいので、auth_digest モジュールを Apatche に追加し、~kazu/ へ .htaccess ファイルも設定して private_html にアクセスしたときは Digest 認証がかかるようにした。ちなみに以前は Base 認証だったので多少セキュリティレベルが上がっている。

ここでタイトルの件が起こった。private_html に Digest 認証をかけたら public_html/ でのインデックス表示に private_html が表示されなくなったのだ。.htaccess をはずしたり、空の .htaccess をおいたりした場合は、問題なく表示される。

以前、Apache1.3 系列を使っていて、そこで Base 認証をかけていたときは、普通に private_html のリンクがインデックス表示されていたのだが。。。

Apache2 の新機能で認証がかかっているリンク先へのシンボリックリンクはインデックス表示されないようになっているのだろうか?それとも単なるバグなのか?

いったん、その認証がかかったリンク先にアクセスし、また戻ってくると、今度はそのリンクがインデックスに表示されるようになった。。。

Wednesday, September 27, 2006

JMeter でのスレッド数、Ramp-Up 期間、ループ回数の解釈

133.217.248.30 - - [27/Sep/2006:18:10:57 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:10:57 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:10:57 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:10:57 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:10:57 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:10:57 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:07 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:07 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:07 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:07 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:07 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:07 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:17 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:17 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:17 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:17 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:17 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032
133.217.248.30 - - [27/Sep/2006:18:11:17 +0900] "GET /inv/inv?id=kajikuni&aid=20000&method=getQuote&args=4%2C3553.T%3ACVQBI%2C3789.T%3ACVQBI%2C4464.T%3ACVQBI%2C4726.T%3ACVQBI HTTP/1.1" 200 3032



スレッド数:3
Ramp-Up 期間(秒):30
ループ回数:6

にした場合のログが上のようになる。

ここから分かることは、30秒間に3つの割合でスレッドが作成される。
各々が6回テストを実行することを指定しているということだ。

結果として、10秒ごとに作成されたスレッドが6回のテストを実行する。

最初に3つのスレッドが1度に作成され、各々が30秒の間に6回のテストを実行する、
つまり、3ユーザが5秒に1回リクエストを出すことをシミュレーションしているのでは
ないということだ。

どちらもテスト総数と1秒当たりのテスト回数は同じになるが、
この違いが大きな違いになるような場合もあるだろうから気をつけること。

Saturday, September 23, 2006

暗号技術のはなし(6)

拡散と混乱を組み合わせた暗号を、「合成暗号」とよぶ。そして、拡散と混乱に使う各アプリケーションを「ラウンド関数」とよぶ。これらラウンド関数を何回も繰り返して適用することでより複雑な合成関数にしたものを「反復合成関数」とよぶ。DES は反復合成関数だ。

強固な暗号方式を使った暗号は、暗号文を統計分析してもパターンを見出すことができず解読することができない。そして潜在的な暗号化キーの数も膨大なため、総当り攻撃では現実的な時間で攻略することができない。したがって、安全ということができる。

暗号は、暗号化方式と暗号化キーの組み合わせである。そのうち秘密なのは暗号化キーだけである。方式そのものは公開しなければならない。これを「ケルクホフの原理」という。方式そのものはスパイや暗号製作者の買収などにより漏れることがあるため、暗号の安全性は方式が秘密であることに頼ってはいけない。キーが秘密であることに頼るべきである。したがって、キーは交換が容易でなくてはならないことになる。

暗号技術のはなし(5)

換字暗号では、平文の統計的なパターンが暗号文に残ってしまう。ヴィゲネーレ暗号のようなポリアルファベティックでもキーの長さは有限なわけで、まだ十分ではない。

換字に転置を組み合わせても、ある文字が別の文字に対応しているという関係は残るわけで、パターンを十分に隠蔽できるわけではない。チンパンジーがタイプしたように完全にランダムに見える暗号文を作成したいのだ。

1つの文字を複数のコンポーネントからなるものに変換したらどうか?これをするのが、「ポリビウス暗号」。

ポリビウス暗号では、文字を2桁の数字に変換する。a を 11、b を 23 とかいうように。5x5 の表をつくり、そこにアルファベットを記入していき、座標を暗号文に使う。

これだけだと、単なるモノアルファベティックな換字暗号だが、これをさらに転置する。そのとき、2桁の数字を1つ1つに分割して転置する。たとえば、e が 15 に変換されているとしたら、1 と 5 を別々に転置する。こうすると、出現頻度がもっとも高い e に対応している 15 が暗号文中にもっとも高い頻度であらわれるというような統計的パターンを隠すことができる。

現代のコンピュータを使った暗号では、文字は複数のビットで表現されるから、各ビットを転置することで同じことをより効果的に実現できる。

このようにして、平文のもつ統計的なパターンを暗号文全体にうまく分散させて隠すことができる。これを「拡散」とよぶ。これにより、暗号文を統計分析してパターンを見出し、解読するということが困難になる。

拡散とは別の概念に「混乱」がある。これは、暗号方式を既知として、それを使った暗号文から暗号化キーを推測できないようにすることだ。これは複雑な換字方式を考案することに相当する。たとえば、現代の DES 暗号では、非線形の換字暗号方式を採用することでこれを実現している。

暗号技術のはなし(4)

換字暗号方式とは別の方式として、「転字暗号方式」というのがある。これは、平文の文字の位置を入れ替えるというもの。

単純な転置、たとえば、転置表に平文を横方向に書いていき、それを縦方向に読んだものを暗号文にする、などではすぐに暗号文に残ったパターンを見抜かれてしまう。この場合なら、何文字かごとに読んでいく、というのを試していけば解読されてしまうだろう。平文に含まれているキーワードや出現頻度の高い単語に注目すると解読がしやすくなる。

暗号文に単純なパターンを残さないために、転置を1回ではなく何回も繰り返したり、転置と換字を組み合わせるなどして、パターンを発見しにくくすることができる。

たとえば、現代の暗号 DES(Data Encryption Standard)でも、転置と換字を組み合わせている。

Thursday, September 21, 2006

暗号技術のはなし(3)

平文の言語学的なパターンが暗号文にそのまま残ってしまうというモノアルファベティック暗号の欠点をなくすには、平文を暗号化するとき、1種類の文字が複数の種類の別の文字に換字されるようにすればいい。たとえば、a という文字がある場所では e に、別の場所では t に変換されるというようにすればいい。

このような換字暗号を「ポリアルファベティック暗号」という。その1つが「ヴィゲネーレ暗号」だ。

シーザー暗号では使用できる暗号化キーのうち1つだけを使って平文を暗号化していく。1つのキーで平文のすべての文字を変換するのでモノアルファベティックになってしまうのだ。ヴィゲネーレ暗号では、複数のキーを使って暗号化をおこなう。たとえば、キー番号 3, 25, 14 を使って暗号化するのであれば、平文の1番目の文字はキー番号 3 で、2番目の文字はキー番号 25 で、3番目の文字はキー番号 14 で変換する。以降、これを繰り返していく。こうすると、平文中で同じ文字でも、出現する場所によって異なる3つの文字のいずれかに変換されることになる。使用するキーの数を増やせばヴィゲネーレ暗号の強度は増す。複数のキーを1まとめにしたものを1つのキーにしているので、これはキーの長さということができる。

ヴィゲネーレ暗号のようなポリアルファベティック暗号では、平文の言語学的なパターンはモノアルファベティック暗号よりは隠蔽されている。したがって、「単純な」統計分析では破ることはできない。

暗号技術のはなし(2)

最古の暗号の1つが、シーザー暗号。これは平文を構成するアルファベットを何文字か後ろにずらして暗号文をつくる。こういうのを「換字暗号」という。

シーザー暗号の暗号化方式は、「文字をずらす」、つまり「n 番目のアルファベットを n + k 番目のアルファベットに換える」ということだ。数学的にいうと「加算」ということになる。暗号化キーは、何文字分ずらすかということであり、先の k がキーに相当する。

アルファベットの文字数は 26 なので、この方式だと暗号化キーの数は 25 種類しかなく、総当り攻撃で容易に解読できてしまう。

暗号化キーの数を増やすためには、上のようにアルファベットをシフトするだけでなく、アルファベットの並びを変更した換字を使えばいい。これだと暗号化キーの数は 25! 通り存在する。26! でないのは、a が a に換字されるというように同じ文字に換字されることがないことを保証したいためだ。

これで総当り攻撃で解読することは難しくなったが(むろんコンピュータを使えば非常に簡単だが)、十分ではない。この暗号の一番の問題点は、平文と暗号文の中の文字が1対1に対応しているということだ。これにより、文字の出現頻度などの言語学的なパターンがそのまま暗号文に残ってしまう。したがって、暗号文を統計的に分析することにより、キーが分からなくても解読できてしまうというわけだ。いうなれば、暗号方式そのもの、錠前が弱いということになる。

このような、平文と暗号文の文字が1対1に対応する換字暗号を、「モノアルファベティック暗号」とよぶ。

Wednesday, September 20, 2006

暗号技術のはなし(1)

暗号は、「暗号化方式(アルゴリズム)」と「暗号化キー」によって構成される。暗号化方式は共通でも暗号化キーを切り替えることで、同じ平文からまったく異なる暗号文に変換することができる。

暗号解析者は、キーをもっていないため、統計学的な分析などを行って暗号文を解読しようとする。これが成功すれば、同時に暗号化キーも判明することが多い(のだろう)。

このようにして解読できる暗号というのは、暗号化方式が弱いのだといえる。暗号化方式が十分に強力なら、解析者はすべての暗号化キーを試す「総当り攻撃」で解読するしかない。キーの数が膨大で、総当り攻撃で解読するのに十分な時間がかかるのであれば、その暗号は安全だといえる。

セッションID を URL に含めると、Referer ヘッダで外部に漏れてしまう件

Web アプリケーションのセッション管理に使うセッションID を Cookie ではなく、URL に埋め込んでいる場合、HTTP リクエストヘッダの Referer でそれが外部に漏れてしまう危険がある。

Web ページ A があり、そこに Web ページ B へのリンクが張られているとする。このとき、ブラウザで B へのリンクをクリックすると、ページ A の URL が Referer ヘッダでページ B の Web サーバへと送られてしまう。したがって、A の URL にセッションID が埋め込まれていて、ページ A と B の管理者が異なっている場合は、セッションID が外部に漏れてしまうということになる。

ちなみに、J2EE の HttpServletResponse#encodeURL メソッドで指定された URL にセッションID を埋め込むとき、指定された URL が自ドメインでないなら、セッションID を埋め込むことをしない。もちろん、これは、上の Referer ヘッダによるセッションID 漏洩を防ぐものではない。

リンクでないなら、Referer ヘッダが送られることはないが、ブラウザのバグで Referer が送られてしまうこともあるらしい。

以上より、セッションID を URL に埋め込むのはなるべく避けた方がいい。


http://www.studyinghttp.net/header#Referer
http://securit.gtrc.aist.go.jp/SecurIT/advisory/webmail-1/
http://securit.gtrc.aist.go.jp/SecurIT/advisory/referer-2/

Friday, September 08, 2006

JMeter のリモート実行で java.rmi.ConnectException: Connection refused to host: 127.0.0.1 が発生する場合

JMeter は Jakarta プロジェクトで開発が進められているパフォーマンス計測用のツール。

クライアントマシンで実行することもできるが、多くのスレッドを起動したり、大量のリクエストを飛ばすので、テスト対象のサーバが限界に達する前にクライアントマシンやネットワークが過負荷になったり、会社などで実行するとネットワークを管理している部署から苦情が来たりする。

こういう場合、JMeter のリモート実行機能を使えばいい。テスト計画を手元の PC 上の JMeter で作成し、それをリモートのおそらく PC よりも処理能力の高いサーバ上にある JMeter で実行する。リモートの JMeter は手元の PC の JMeter で管理する。

このために、JMeter を実行するリモートサーバで次のようにする。


$ export CLASSPATH=$CLASSPATH:$JMETER_HOME/lib/ext/ApacheJMeter_core.jar:$JMETER_HOME/lib/jorphan.jar:$JMETER_HOME/lib/logkit-1.0.1.jar

$ rmiregistry

$ jmeter -n -s


リモート実行では、RMI を使う。rmiregistry は、ネットワークオブジェクトの位置を登録したり問い合わせたりするサービス。これは、JDK に付属しているツールで、デフォルトで 1099 ポートをオープンする。-n で非 GUI モード、-s でサーバモードで JMeter を起動する。現在、非 GUI モードでも X-Window 関係のライブラリは必要となっている。

クライアント PC では、jmeter.properties ファイルの remote_hosts でリモートサーバを指定してから、JMeter を起動する。

そのようにしてから、JMeter のリモート実行を開始したのだが、うまくいかなかった。PC の jmeter.log を確認してみると、


java.rmi.ConnectException: Connection refused to host: 127.0.0.1


が発生していた。これは、rmiregistry がネットワークオブジェクトのメソッドコール用のアドレスとして自分のローカルアドレス 127.0.0.1 をクライアント PC に通知しているために起こっていると考えられる。リモートサーバの /etc/hosts ファイルを調べてみると、


127.0.0.1 localhost mail


というエントリがあった。mail が DNS にも登録されている名前なのだが、これをこのエントリから削除すると、さっきの例外は発生しなくなった。rmiregistry がネットワークオブジェクトのメソッドコール用として使うポートは 1099 ではなく、ランダムに選んでいるようなので、もし、リモートサーバで Firewall を実行しているなら、クライアント PC からの接続に関してはすべてのポートを許可するようにしておかなくてはならない。

実は、このようにしてもリモート実行はうまくいかなかった。jmeter.log を見ると、リモートサーバがソケットをクローズしてしまうらしい。仕方ないので、リモートサーバにテスト計画(.jmx ファイル)をコピーし、そこで直接 JMeter を実行して、テスト結果のログ(.jtl ファイル)をクライアント PC の JMeter の Listener に指定して表示させた。


$ jmeter -n -t TestPlan.jmx -l Log.jtl


-t オプションでテスト計画のファイルを指定し、-l オプションでログファイルを指定する。このログファイルをクライアント PC の JMeter のリスナーで読み込んで表示する。

ただ、テスト計画ファイルに日本語が含まれていると、ログファイルが文字化けしてしまう。これが原因となってリスナーで読み込むときにエラーになるときもあるので、テスト計画ファイルを作成するとき、テスト計画名などはすべて英語にし、メニューバーのオプションで言語も英語にした方がいい。

Thursday, August 31, 2006

Oracle でちょっとしたパフォーマンスチェック

IBM xSeries 336 上の Oracle 10g の SqlPlus で、


select to_char(sysdate , 'YYYYMMDD HH24:MI:SS') from dual;
select 1 from dual;
-- 同じ文を 4640 個
select to_char(sysdate , 'YYYYMMDD HH24:MI:SS') from dual;


としたスクリプトを実行すると、だいたい5秒かかった。次に


select to_char(sysdate , 'YYYYMMDD HH24:MI:SS') from dual;
select 25646 from dual;
-- 同じ文を 4640 個。ただし、乱数になっている。
select to_char(sysdate , 'YYYYMMDD HH24:MI:SS') from dual;


とすると、だいたい20秒かかった。ちなみに乱数は、シェル変数の
$RANDOM を使って、シェルスクリプトで上のファイルを生成した。

SQL 文がまったく同じなら実行計画が再利用されることになるので、最初のスクリプトは5秒しかかからなかった。2番目のスクリプトは、実行計画がその都度、立案されるので時間がかかっている。

つまり、4641 件の SQL の実行計画に 15 秒かかったというわけだから、この SQL の実行計画には 3 ミリ秒ほどかかっていることになる。

実は、この実験は、いまやってる仕事で株の銘柄キーワード検索のパフォーマンスを調べる過程で実行した。MEIGARA_K_TBL に銘柄情報と、それを検索するためのキーワード(正式名称、正式カナ名称、英語名称、略称をタブ区切りにしたカラム)をセットして、


select to_char(sysdate , 'YYYYMMDD HH24:MI:SS') from dual;
SELECT SHOUKEN_CODE || '.' || MARKET_CODE
FROM MEIGARA_K_TBL
WHERE VALID_FLAG = 1 AND INSTR(KEYWORDS, 'イノテック') > 0 ORDER BY SHOUKEN_CODE;
--
-- キーワード部分を変えた文を 4640 個
--
select to_char(sysdate , 'YYYYMMDD HH24:MI:SS') from dual;


としたスクリプトを実行した。この結果はだいたい 40 秒であった。キーワードが各々異なるので実行計画はその都度、立案されていることになる。先ほどの SQL 文の実行計画を立てるのと同じだけの時間がかかっているとすると、4641 回の文字列部分一致検索に 25 秒だけかかっていることになる。

つまり、1回あたり、5 ミリ秒ほどだ。Oracle Text を使えばもっと速くなる余地はありそう。

ちなみに、部分一致検索では、LIKE 検索を使うよりは、ここでのように関数を使った方が速い。

Oracle の空文字列は NULL と見なされてしまう件

Oracle の VARCHAR2 型には問題があって、空文字列を NULL 扱いしてしまう。たとえば、


SQL> create table hoge (
2 str varchar2(88)
3 );

SQL> insert into hoge values ('');

1 row created.

SQL> select * from hoge where str = '';

no rows selected

SQL> select * from hoge where str is null;

STR
--------------------------------------------------------------------------------


SQL> create table boke (
2 str varchar2(22) not null);

Table created.

SQL> insert into hoge values ('');
insert into hoge values ('')
*
ERROR at line 1:
ORA-01400: ("TOMITA"."BOKE"."STR")にはNULLは挿入できません。



のようになってしまうのだ。これは、古くからあるバグのようで、修正するとアナウンスはされていたようだが、いまだになおっていない。

Friday, August 25, 2006

ハードディスクからのデータの完全消去

Live CD で Linux をブートして、ハードディスクを dd コマンドで書きつぶす。

df コマンドで表示される Filesystem に対して dd を実行する。たとえば、


tomita@mail:/etc$ df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/cciss/c0d0p3 68990656 4486156 64504500 7% /
tmpfs 1030916 0 1030916 0% /dev/shm
/dev/cciss/c0d0p1 122339 18502 97310 16% /boot
tmpfs 10240 64 10176 1% /dev


であれば、


dd if=/dev/zero of=/dev/cciss/c0d0 bs=1M


を実行すればいい。むろんスーパーユーザで実行する。こうすると、ディスクの盤面を 0x00 で埋め尽くす。

ちなみに Live CD 用のイメージをダウンロードして、それを CD に書き込むときは、イメージとして書き込まなければならない。普通に書き込むと、単なるファイルとしてイメージファイルが書き込まれてしまう。Windows なら Record Now などのツールを使わなければならない。

いまは、dd コマンドではなく shred コマンドを使った方がいい。dd と違い、ランダムな数値で書き潰してくれるので、より復元が困難になる。


shred -v -n 1 /dev/cciss/c0d0


のようにすれば、指定したデバイスをランダムな数値で1回書き潰してくれる(-n オプションで回数を指定)。回数を多くすればするほど復元しにくくなる。

Friday, August 11, 2006

XML, HTML と DTD

フォーマットが正しい XML 文書とは、開始タグが終了タグで閉じられている、属性が引用符で囲まれている、すべてのエンティティが宣言されている、などの条件を満たす文書のこと。

それとは別に、妥当な XML 文書とは、DTD を宣言し、その中で定義されているルールにしたがっている文書のことだ。

たとえば、Web アプリケーションの配備記述子である XML 文書は、次のように開始されている。


<?xml version="1.0" encoding="EUC-JP"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">


http://java.sun.com/dtd/web-app_2_3.dtd が DTD 文書である。これが、この XML 文書で使用できる合法的な構成要素を定義している。この文書には、

<!ELEMENT web-app (icon?, display-name?, description?, distributable?,
context-param*, filter*, filter-mapping*, listener*, servlet*,
servlet-mapping*, session-config?, mime-mapping*, welcome-file-list?,
error-page*, taglib*, resource-env-ref*, resource-ref*, security-constraint*,
login-config?, security-role*, env-entry*, ejb-ref*, ejb-local-ref*)>


のような定義がある。これは、配備記述子である XML 文書のルート要素が web-app であることと、それの子要素と多重度、子要素が親要素の中で出現する順番を定義している。ただ、Web コンテナがこの配備記述子を解釈するときに、URL で指定された DTD を読み込んでいるわけではないようだ。Web コンテナは最初から配備記述子のルールを知っているはずだからだ。このようにあらかじめ文書の定義について合意ができていれば、DTD は必要ではないはずだ。実際、XML では DTD は必須ではない。

HTML でも DTD を宣言することができる。たとえば、HTML 文書の先頭には

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">


を宣言することができる。これは、W3C が定義している HTML 4.01 の文書型定義(DTD)に準拠していることを宣言している。ブラウザによっては、この DOCTYPE 宣言を見て、HTML や CSS 解釈の挙動を変えたりする。宣言されていれば、HTML/CSS の仕様どおりの解釈をし、されていなければ過去の慣習的な「正しくない」解釈をする。Firefox や IE 6.0 は、この「DOCTYPE スイッチ」が機能する。

Saturday, July 22, 2006

J2EE アプリケーションを構成する層(レイヤ)

J2EE アプリケーションを構成する層(レイヤ)は以下のように分けられる。


  • Web 層
  • アプリケーション層
  • EIS(Enterprize Infomation System)層


Web 層はプレゼンテーション層といえる。HTTP 等のリクエストを受け付け、アプリケーション層が提供するビジネスロジックを呼び出し、レスポンスを返す。

Web 層を実現するのが、サーブレットコンテナなどだ。Tomcat などがそうだ。

前述したように、ビジネスロジックを実装しているのがアプリケーション層だ。EJB コンテナなどが、これに該当する。アプリケーションサーバとよばれ、IBM の Websphere とか BEA の WebLogic などの製品がある。これらはサーブレットコンテナも含んでおり、Web 層を兼ねることもできる。

EIS 層にあるのが、リレーショナルデータベースや CRM(Customer Relationship Management)などであり、エンタープライズリソースへのアクセスを提供する。

これらの層は、物理的に分かれている場合もあれば、1台のサーバの中で論理的にのみ分かれている場合もある。

Web 層とアプリケーション層との通信には、通常 RMI over IIOP(RMI/IIOP)が使われる。RMI は Java に特化したリモートメソッド呼び出しの規約であり、IIOP は言語やプラットフォームに依存しない分散コンポーネントどうしの TCP/IP を使った通信プロトコルである。IIOP 上で RMI を使用することにより、Java 以外のコンポーネントともやり取りができるようになる。

EIS 層とアプリケーション層は、EIS 層のリソース固有のプロトコルを使って通信する。EIS 層の RDB と通信するなら、JDBC ドライバが使用されるだろう。

小規模なアプリケーションだと、アプリケーション層が存在しないことがある。つまり、EJB コンテナなどが使用されないことがある。この場合、Web 層でビジネスロジックを実装することになるのだが、プレゼンテーションとビジネスロジックをきちんと分けた方がよい。適切なデザインパターンを使えば実現できる。

たとえば、DTO パターンは、アプリケーション層と Web 層との通信に、ビジネス層の疎粒度のビューとなる DTO(Data Transfer Object)を使うというものだ。これにより、通信量を減らすことができた。この DTO に相当するものを Web 層でビジネスロジックを実装するときにも使うことができる。

DTO は、POJO である Java Bean だ。

フレームワークとは?


  • フレームワークとは、特定のソフトウェア問題を解決するクラスやインターフェースの集まり であり

  • それらのクラスやインターフェースは、何らかの抽象概念を表す。

  • フレームワークは、ハイレベルのパターン、つまりアーキテクチャを構成しており、これにより、上の抽象概念を表現するクラスやインターフェースの相互作用を定義している。


フレームワークをアプリケーション開発に使用する場合、そのアプリケーションはフレームワークが解決する『特定のソフトウェア問題』に合致していなくてはならない。つまり、アプリケーションはフレームワークが提供するアーキテクチャに相応しいものでなければならない。その場合に、フレームワークが構成しているアーキテクチャを再利用できるということだ(アーキテクチャの再利用)。

アプリケーション開発者は、アプリケーションの特定の機能を実現するために、フレームワークを構成している抽象概念を拡張・実装することができる。したがって、これらの抽象概念を表現しているクラスやインターフェースは、フレームワークを拡張する拡張点とよぶことができる。

いうなれば、フレームワークとは、不完全なアプリケーション であって、アプリケーション開発者は、その不完全な部分を補うことでアプリケーションを開発する。フレームワークが提供する部分は再利用することができるので、その分、負担が軽減するというわけだ。

ライブラリとの違いは何か?ライブラリにはアーキテクチャは含まれない。これが大きな違いだ。

フレームワークはアーキテクチャを含んでおり、アプリケーションはそのアーキテクチャを再利用する。アーキテクチャは抽象概念の相互作用を定義しており、アプリケーションが拡張した抽象概念は、そのアーキテクチャにしたがって制御されることになる。

ライブラリの場合はアプリケーションのコードからライブラリが呼ばれるのに対して、フレームワークでは、フレームワークからアプリケーションのコードが呼ばれる。このことを 制御の反転 とよぶ。

Saturday, April 29, 2006

ドメインのレジストラへの登録

以前、goo ブログに書いておいた内容をここに新たにまとめる。

fortunefield.com ドメインの取得に Yahoo のサービス
使った。

自分でネームサーバも立てたので、それのレジストラへの登録も
する必要があるが、これも Yahoo のサービスから行った。

自分のゾーンに権限をもつネームサーバを2つ以上登録しなければならない。
これは冗長性をもたせるためのインターネットの規約である。

このネームサーバは、「登録済み」 のものを指定しなければならない。
レジストラには、登録済みのネームサーバのホスト名と IP アドレスが
あって、その中から指定しなければならないということだろう。

したがって、ここにはすでにネームサーバとして機能しているサーバを
指定すればいい。むろん、そのサーバに自分のドメインのゾーン情報が
定義されていなければ無意味だが。そして、この他人のネームサーバを
勝手に指定することを阻止するようなチェック機構はないのではないかと
シマリンは語っていた。

自分で立てたネームサーバを指定するには、まず、そのネームサーバを
登録しなければならない。これを Yahoo のサービスでおこなうには、
Yahoo のヘルプから登録情報を送信しなければならない。送信後、5営業日くらいで
レジストラへの登録が終了するようだ。その後で、Yahoo のドメインコントローラ
を使って、自分で立てたネームサーバを指定することになる。

Friday, April 28, 2006

Oracle メモ -- 起動・終了と表領域の更新

$ sqlplus /nolog
> connect user_name as sysdba

で Oracle に入る。user_name は SYSDBA の権限をもつユーザ名。
昔は、connect internal が使えたが Oracle 9 あたりから使えなくなった。

> shutdown

で終了。immediate 等のオプションもある。

> startup

で起動。これにも、どこまで起動するかを決めるオプションがある。

OS 起動直後などの場合、リスナーを立てる必要もあるので

> lsnrctl start

も実行する。



> ALTER DATABASE TEMPFILE '/home/oracle/app/oradata/temp.dbf' OFFLINE;

これで一時表領域に割り当てられた一時ファイルをオフラインにできる。

> ALTER DATABASE TEMPFILE '/home/oracle/app/oradata/temp.dbf' DROP INCLUDING DATAFILES;

これで一時ファイルを削除し、

> ALTER TABLESPACE TEMP ADD TEMPFILE '/home/oracle/app/oradata/temp.dbf' SIZE 2048M AUTOEXTEND OFF;

これで再作成する。AUTOEXTEND を ON にすれば、指定した SIZE に達すると自動拡張
される。

こういう操作をいちいちしなくて済む方法はないのか?
必要ない領域は削除して、自動的に一時ファイルのサイズが小さくなるような。

Sunday, April 02, 2006

メールと Web ページの文字エンコーディング

メールでもっとも単純な場合は、メールヘッダに

Content-type: text/plain; charset=ISO-2022-JP

のようなヘッダが入る。これにより、そのメールで
使われている文字エンコーディングが判明し、
メーラはメール本文を表示することができる。



Web ページの場合、HTTP レスポンスヘッダに

Content-type: text/plain; charset=UTF-8

のようなヘッダがあれば、ブラウザはここで指定された文字エンコーディングで
ページ内容を解釈して表示する。

Web サーバは、text/plain や text/html のような MIME タイプについては、
ブラウザからリクエストされたファイルの拡張子にもとづいて判別できる。
たとえば Apache だと、mime.types ファイルで対応関係を定義している。

問題は、charset で指定される文字エンコーディングの方で、これは
Web サーバが判定するのは難しい。そこで、HTML ファイルの場合は、
meta タグによって、この問題を解決している。つまり、
HTML ドキュメントで

<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=euc-jp">
</head>

のように head セクションの最初に書く meta タグで文字エンコーディングを指定する。
ブラウザはここで指定されているエンコーディングにしたがって、ドキュメントを
解釈し、表示する。本当は、Web サーバがここを見て HTTP レスポンスヘッダを
設定してくれるのがベストだが、そこまではしてくれない。
もちろん、この手法は HTML ファイルにしか使えない便法にすぎない。

やはり、もっともよいのは HTTP のレスポンスヘッダで charset を指定してやることなのだ。
これを簡単にやる方法として、たとえば Apache だとディレクトリに
.htaccess というファイルをつくって、そこに

AddType "text/html; charset=EUC-JP" html

と書いてやると、そのディレクトリとサブディレクトリ以下に置かれた拡張子が
html のファイルの HTTP レスポンスの Content-type ヘッダの値を指定することが
できる。この方法だと、HTML 以外のファイルにも charset を指定することができる。
HTML の文字符号化スキーム を解説した文書を参考にしてくれ。


Web サーバは、
Content Negotiation
という処理をしてくれることがある。
これは HTTP/1.1 の規格で定められている。この処理は、
ブラウザからのリクエストヘッダ Accept-Language や Accept-Charset の値から
適切な言語、適切な文字エンコーディングのドキュメントを選択して
レスポンスするというものだ。たとえば、index.html として複数の
index.html.en, index.html.fr, index.html.ja.jis, index.html.ru.cp-1251
等々を用意しておき、リクエストヘッダを使って Content Negotiation して
適切なファイルをレスポンスする。この場合は、HTTP レスポンスヘッダに
文字エンコーディングが入ることもあるだろう。

ジョエル・テスト:いいプログラムへの12のステップ

1.ソース管理システムを使っている?

CVS 等のこと。これがないと複数のプログラマの作業を調整することができないし、
バージョンを戻したりとか、複数のバージョンを管理したりとかいったことが
できない。

2.1ステップでビルドできる?

1ステップで、スクラッチからビルドして、ダウンロードサイトだろうと
CD-ROM のインストールだろうとリリースできるものができるということ。
#ifdef や各国語バージョンもすべて作成できなければならない。

3.デイリービルドしてる?

デイリービルドすることによって、ビルドできないソースを放置することが
なくなる。いい方法は、昼休みにデイリービルドし、うまくいけば昼休み後に
みんなが最新のソースをチェックアウトし作業を続ける。失敗したら、
チェックアウトせずに以前のソースで各々作業を続ける。もちろん、ビルドが
うまくいくように修正作業もおこなう。

4.バグデータベースはある?

・バグを再現する手順
・期待される(正しい)ふるまい
・観察される(バグの)ふるまい
・誰に割り当てられているか
・修正済みか否か

この5つは最低でも管理しなければならない。Bugzilla を使うとよい。

5.新しいコードを書く前にバグを直してる?

バグを直すのが遅ければ遅いほど、時間もコストもかかるので、できるだけ速く
修正するということだ。また、バグがなければ、直ちにリリースすることだって
できるのだ。

6.アップデートされてるスケジュールがある?

デモや広告など、ビジネス上の計画を立てるためスケジュールは必要だ。
また、スケジュールを立てることによって、実装すべき機能を明確にしなればならず、
それにより、あまり重要でない機能を先に実装してしまうなどということを
避けられる。

7.仕様書はある?

アジャイルな開発手法だと仕様書がないというか、実装しながら
設計していくというやり方のようだが。。。

いずれにしても、いきなりコードを書き始めるというやり方はまずいのだ。
最初から実装レベルの細かい仕様書を作る必要はないし、作れないだろうが
おおまかなレベルの仕様書は書く必要がある。

8.プログラマは静かな環境で働いてる?

ゾーン(集中状態)に入るには時間がかかる。よって、静かな環境で
働く必要がある。理想的には個室ということだが、なかなか難しいかな。


9.手に入る最高のツールを使ってる?

プログラマがストレスを感じないようにするということだ。
よりよいツールを使えば、生産性も上がる。

10.テスタはいる?

TDD を実践してるときは、少なくとも単体テストはプログラマがやるだろう。
統合テストやユーザビリティテストでは、より時給の安いテスタを使え、って
ことだな。

11.採用前にコードを書かせてる?

当然のことだが、なかなかやってるところは少ないかな。
履歴書と世間話で決めてるところが大半かな。

12.ユーザビリティテストをしてる?

GUI プログラムではこれが重要。ユーザビリティによってユーザに
受け入れられるかどうかが決まってくる。
いわゆる『廊下でのユーザビリティテスト』で十分。5人に使ってもらえれば
95%の問題点は分かる。

Sunday, March 26, 2006

malloc, realloc の効率的な使い方

malloc() でメモリを確保する場合、2^n 単位で確保するとよい。
無駄になるメモリはたかだか50%だし、変な断片化も最小化される。

とのことだ。断片化については数学的な議論をして確認したいところだ。

また、realloc() についても、以前に割り当てられていたサイズの2倍のサイズを要求すると、log(n) 回より多く realloc() を呼ぶこともないし、巨大な文字列に大しても適度なパフォーマンスが得られるし、無駄になるメモリもたかだか50%で済む。

とのことだ。これも数学的に確認したいところだ。

以上は、『Joel on Software』の第2章で触れられていたことだ。

Tuesday, March 14, 2006

java.sql.Array を使う方法

これはインターフェースなので、java.sql.Connection 同様、JDBC ドライバのベンダから実装を提供してもらわねばならない。

Oracle では、oracle.sql.ARRAY がこのインターフェースの実装になっている。次のようなコードを使う。

oracle.sql.ArrayDescriptor arrayDesc = oracle.sql.ArrayDescriptor.createDescriptor("HOGE_ARRAY", con);
java.sql.Array array = new oracle.sql.ARRAY(arrayDesc, con, new int[] {1,3,5,0});

con は java.sql.Connection である。これは、Oracle のドライバから取得した Connection でなくてはならない。DataSource 等から取得したラッパだと ClassCastException が発生することがある。

HOGE_ARRAY は、Oracle の CREATE TYPE 文でつくった型だ。何故か大文字で指定しなくてはならなかった。

ここでは、整数配列から java.sql.Array をつくっているが、当然、HOGE_ARRAY 型は整数を格納できる配列型として Oracle 側で定義していなくてはならない。

Tuesday, January 17, 2006

Oracle SQL の DISTINCT でエラー

Oracle の SQL で DISTINCT を使って重複行を削除しようとしても
SELECT のカラムに LOB 型があると ORA-00932 というエラーが出る。
これは型が不適合というエラーのようだ。

LOB 型を ORDER BY 句や WHERE 句の中で使っても
同様のエラーが出る。

LOB 型を単純に比較することができないのでこのエラーが出るのだろう。

Oracle のスクリプトで空行によるエラーが出るとき

Oracle でスクリプトを走らせる場合、SQL 文の途中に空行があると
SP-0734 というエラーが出てしまう。これは SQL*Plus で
SQL 文をコピー&ペーストしたときも同じ。ペーストした
SQL 文の途中に空行があると同じエラーが出てしまう。

SQL*Plus 内で

@hoge.sql

を実行したときは問題ないのだが、

sqlplus username/password@orcl <hoge.sql

のようにしたときにはこの問題が出る。

Monday, January 16, 2006

Oracle 10g における VIEW の外部結合でエラー

Oracle 10g で VIEW をつくって、それらを完全外部結合したら
ORA-03113 エラーが発生してしまう。ネットで調べてみると
同様の現象が報告されていて、どうも Oracle のバグらしい。
Oracle 再起動で直ることもあるとか。非力なマシンで
やっているので、もっとマシなサーバでやればうまくいくのかもしれない。

とりあえず、VIEW をテーブルにコピーして、テーブルを
外部結合させたらうまくいった。

SELECT top.seq_no top_news_grp,
general.seq_no general_news_grp,
stock.seq_no stock_news_grp,
money.seq_no money_news_grp
FROM
v_top_news_grp top
FULL OUTER JOIN
v_general_news_grp general ON ( top.seq_no = general.seq_no )
FULL OUTER JOIN
v_stock_news_grp stock ON ( top.seq_no = stock.seq_no OR general.seq_no = stock.seq_no )
FULL OUTER JOIN
v_money_news_grp money ON ( top.seq_no = money.seq_no OR general.seq_no = money.seq_no
OR stock.seq_no = money.seq_no );

Blog Archive