旧文書

プログラミングとかサーバー管理とか

現在、扱っているサーバーが Linux にシフトしたので、今後は RedHat Linux な情報をお伝えします。近いうちに Solaris の情報はちょっと遠くへ旅に出ます。

かなり自分用の覚え書き。でも、コメントがあれば、修正なりお返事なりします。

このファイルは古いものですので、今ではもう通用しないかも知れません。予めご了承下さい。

もくじ

JSP (Tomcat 4.0.1/3.3 + webapp)

JSP は、 Servlet を HTML に組み込み、ユーザーからのリクエストに応じて、 サーバー側で処理した結果を返す仕組みです。 Servlet は Java の中に HTML を書くのでデザイン的に解りづらいですが、 JSP だと PHP のように HTML 中に Java ソースがあるので、 見通しがよいというメリットがあります。 また、JSP は一度目のリクエスト時に Servlet にコンパイルされてメモリ上に 展開したままにされ、二度目以降は更新がなければこの Servlet で代用するので、 処理速度は Java VM を利用するとはいえども、かなり高速です (mod_perl と同等の速度で、PHP や通常の perl-CGI よりは高速という計測結果を どこかで見た覚えがあります)。

Tomcatは、 JSPを扱うことができるサーバーの一種で、そのほかにも GNUjsp とか JRun など、 他にも JSP を扱えるサーバーはたくさんあるようです。

ただ、特に UNIX システム上ではユーザー権限で自由に扱えないなどの問題があるし、 そもそも CGI ほどに知られていないため、対応するプロバイダが少なく、 普及しているとは言えないのが実情で、情報が少ないのは事実です。

mod_webapp のビルドで困る

Tomcat 4.0.3 なんかも出ていますが、最近は JSP どころか Solaris すら使用していないので、最新情報はあまり把握していません。 また、この情報が obsolete になったら、そのうち削除されて新しく PHP の情報になったりするかも知れません。 ご了承下さい。

説明 (README) 通りにビルドすると、apachectl start の時点で

 Syntax error on line 239 of /etc/apache/httpd.conf:
 Cannot load /usr/apache/libexec/mod_webapp.so into server:
  ld.so.1: /usr/apache/bin/httpd: fatal :
  relocation error: file /usr/apache/libexec/mod_webapp.so:
  symbol __divdi3: referenced symbol not found

Tomcat-users ML に投げてみたけど返事なし。

仕方なく自力で調べてみると、どうやら __divdi3 はわり算をするための関数で、 libgcc.a というライブラリの中で定義されている関数らしい。 libgcc.a を探してみると…

wassy> find / -name libgcc.a
/usr/local/lib/gcc-lib/i386-pc-solaris2.8/3.0.1/libgcc.a

どうも libgcc.a がパスに含まれていなかったようなので、改めて mod_webapp.so をリンクし直す。

wassy> ld -G -lm -lsocket -lnsl -ldl -o mod_webapp.so mod_webapp.o \
 ~/source/webapp-modle-20011017/lib/libwebapp.a \
 ~/source/webapp-module-20011017/lib/libapr.a \
 /usr/local/lib/gcc-lib/i386-pc-solaris2.8/3.0.1/libgcc.a

すると、動いた。わーい。

なお、上記の ld のオプションは、Makefile 中 "-o mod_webapp.so" が含まれる行を探し出し、各自で適宜 libgcc.a をフルパスで追加してください。上のコマンドラインをそのままcopy & paste しても環境によっては動作しない、っつーか別のエラーが出るだけなので、悪しからず。(2002/05/13 追記)

今後のためにも、ln -s /path/to/libgcc.a /usr/local/lib/ などとシンボリックリンクを張ると良いかもね。

JSP の日本語の扱い

JSP は日本語の取り扱いがとても複雑。 JSP は Java なんですから、まぁ当然ながら内部的には文字列はすべからく Unicode で処理されます。 ただし、request.getParameter() とか JDBC 関連は Unicode 化されない (JDBC での Unicode 変換はドライバに依存するので注意) ので、ちと困る。 しかも、たいてい QUOTE_STRING は EUC だったり Shift_JIS だったりする。

対策

HTML 自体は utf-8 で書く。 サイトによっては ISO-2022-JP で書けというが、原理的にムリ。 だって、たとえば 「メール」とISO-2022-JPで書いたら「%a!<b>&lt;%<b>k」となり、JSP の開始タグとみなされて、コンパイルエラーになるですよ(泣)。 かといって、さすがに MS_Kanji ってのは…。ねぇ?

ユーザーからのリクエストは全て EUC_JP → Unicode で統一的に取得する。 以下の関数 getUnicodeString を定義部 <%!%> で宣言して利用するとよい。 この関数は、引数で与えられた文字列が既に Unicode に変換済みだと何もしないので、かなり便利がよいかと思います。

Tomcat 3.3 からは getParameter が一部 Unicode を返すようになりました。 …と油断していると、どうも GET method のものだけのようで、POST method で送信したりすると変換されないときもあります。 かなり中途半端な仕様です。 余計ややこしくなってしまいました (泣)。 やっぱりアメリカ人には日本語処理はよく理解できないようです。 まぁ仕様がいい加減で熟慮の微塵もない Unicode ごときで、日本人が数十年悩んできた文字コード問題が簡単に解決すると思っている、Unicode 万歳的単細胞だからですかね。

いや、Unicode の開発には日本人 (某J社 とか) も絡んでいるのは知ってて言ってますけど。

private String getUnicodeParameter(String key) {
  return getUnicodeString(request.getParameter(key));
}
private String getUnicodeString(String s)
  throws java.io.UnsupportedEncodingException {
  char[] buf = s.toCharArray();
  boolean isUnicode = false;
  for (int i=0; i<buf.length; i++) {
    if (buf[i] > 0x100) {
      isUnicode = true;
      break;
    }
  }
  if (isUnicode)
    return s;
  else
    return new String(s.getBytes("8859_1"), "JISAutoDetect");
}

データベース/ファイルからの読み込みは、 EUC_JP → Unicode で統一する (上の getUnicodeString 関数を利用する)。

データベース/ファイルへの書き込みは、Unicode → EUC_JP で統一する。上と同様に、以下の関数 getEucString を利用すると若干便利、つーか 1 行が短くなって見やすいかな。

private String getEucString(String s)
  throws UnsupportedEncodingException {
  return new String(s.getBytes("EUC_JP"), "8859_1");
}

ちなみに EUC_JP と書くのが Java の流儀で、"utf-8" とハイフンでつなぐのは HTTP/HTML の流儀。 この違いを間違えると UnsupportedEncodingException が投げ出されます。 要注意。

ユーザーを信じるな

データベース文字列の検査

データベースにユーザーからの文字列を安易に入れてよいものか。 シングルクォート ( ' ) やアスタリスク ( * )、セミコロン( ; )、ダブルクォート( " )、カンマ( , )、…特殊文字は多い。 我々崇高な大和民族としては、こうした些細なことを視野に入れて悩んだあげく、腹を痛めたり、時にはパニック症候群にでもなりながら、プログラミングをしたいものです (^^;

さて、たくさんエスケープしないといけないような気がしてきますが、本当は実際にエスケープしなければならないのはシングルクォートのみです。 というのは、ワイルドカード (% や _) は where like で検索しない限り有効にはならないし、セミコロン (;) やカンマ (,) はユーザーからのリクエストを常に クォートで囲んでいる限り問題にならないからです。 (まぁ、ユーザーがクエリーを発行できる場合はよく考えないといけませんが…) むしろ、数字と思っていたら悪意のある文字列だったというパターンもありうるので、そちらに気をつけるべきでしょう。

んで、シングルクォートをエスケープする方法が Tomcat-users に流れていた。

Michael Weissenbacher の approach

Tomcat-users ML に流れたやり方。 本当はもう少しパフォーマンスのよい置換があるのでしょうが、ソースの見やすさとメンテナンス性はこちらのほうがよいかも知れません。 C++ ならもっと簡単なのにね。

public static String replaceString(String oldString, String toReplace, String replaceWith) {
  if (toReplace == null || toReplace.equals("") || oldString == null ||
      oldString.equals("") || replaceWith == null)
    return oldString;

  StringBuffer sb = new StringBuffer();
  int oldIndex = 0;
  int newIndex = 0;
  while ((newIndex = oldString.indexOf(toReplace, oldIndex)) != -1) {
    sb.append(oldString.substring(oldIndex, newIndex));
    sb.append(replaceWith);
    oldIndex = newIndex = newIndex + toReplace.length();
  }
  sb.append(oldString.substring(oldIndex, oldString.length()));
  return sb.toString();
}

他にも、PreparedStatement を使うとかの手法があるらしいが、よく知らない。 誰か教えて。

HTML タグの検査

上記の replaceString で > → &gt; に変えてやればよいのかな。 (やる気無し)

perl

天気予報をゲットだぜ

一応、ソース内にコメントして置いたので、多くは語りません。 著作権問題とか絡んできますし。 まぁ、個人で使う分には全く問題はないはずですけど。 一応、簡単な構文解析の例題ということで、プログラミング的見地から掲載しておきます。

ソースはこちら (別ファイルにしました)

小ネタ

while (!sleep) {
   $sheep++;
}

Try this when you cannot sleep.

Courier IMAP over SSL with MySQL

まずは OpenSSL のインストール

普通は、パッケージでインストールするのかも知れない。OpenSSH のインストールで OpenSSL が既に導入されているケースも多い。

でも、こういう個人的には何でもソースからビルドするのが好き。 だって、パッケージによっては --with-charset=ujis としないと日本語をうまく扱えないものもあるし (MySQL とかね)、--with-user で存在しないユーザー名を指定しているかも知れないし、とにかく、どんなコンパイルオプションでコンパイルしたか、わからないでしょ。

さて、OpenSSL をダウンロード。http://www.openssl.org/source/ からソースをダウンロードしてくる。 今回ダウンロードしたのは、できたての openssl-0.9.6b.tar.gz。 これを、gunzip + (un)tar し、./config する。 コンパイルオプションは --openssldir=/usr/ssl (Solarisの標準らしい) のみ。 で、OpenSSL のインストール完了。

つづきは、そのうち。

遅い Windows 2000 を高速化する

1. HDD アクセスを高速にする

速い CPU、大量の RAM、高速なビデオボード…。 それでも、初期状態のまま Windows 2000 を使うとかなり遅く感じられるのは、Windows 2000 のディスクコントローラの初期設定のせい。

Ultra DMA を有効にする

デバイスマネージャから、「IDE ATA/ATAPI コントローラ」を開き、「プライマリ IDE チャネル」「セカンダリ IDE チャネル」両方に含まれるディスクについて、"DMAを有効にする(可能な場合)" を選択する。

ただし、この方法は Windows 2000 Service Pack 2 以降でのみ有効、というか、SP2 適用以前は UDMA 未対応。 自動的に UDMA に切り替わればいいのに…。

短いファイル名 (8.3形式) の互換性を止める

レジストリエディタで [HKEY_LOCAL_MACHINE]->[SYSTEM]->[CurrentControlSet]->[FileSystem] にある、NtfsDisable8dot3NameCreation1 にする。

Windows 2000 は、ご丁寧にも旧システム用に 8.3 形式のファイル名を作ってくれています。

ディスクパフォーマンスカウンタを停止する

ディスクパフォーマンスカウンターが有効化されていると、ある程度の パフォーマンスオーバーヘッドが発生する。

コマンドラインから

  C:\> diskperf -N

とすると、ディスクパフォーマンスカウンタを停止させることができる。

ディスクキャッシュサイズを最大にする

レジストリエディタから、[HKEY_LOCAL_MACHINE]->[SYSTEM]->[CurrentControlSet]->[Control]->[Session Manager]->[Memory Management] を開き、中にある IoPageLockLimit を搭載メモリにあわせて以下のように設定する:

搭載メモリ設定値(16進)設定値(10進)
〜32MB10004096
32MB〜20008192
64MB〜400016384
128MB〜800032768
256MB〜1000065536

初期設定は 0 になっていて、この時 0x200 (512kB) として扱われる。なんと小さなキャッシュであることか!

残念ながら、512MB以上だからといって 0x20000 を指定しても無駄なあがきらしい。 Windows XP なら対応しているのかもしれないが。

フォルダの最終アクセス日時の書き換えをやめる

フォルダを開くたびに、そのフォルダの最終アクセス日時を変更するのはずいぶんとオーバーヘッドになる。 これをやめるには、レジストリエディタから、[HKEY_LOCAL_MACHINE]->[SYSTEM]->[CurrentControlSet]->[Control]->[FileSystem] を開き、NtfsDisableLastAccessUpdate という値 (DWORD) を作って、1 に設定する。

アプリケーション起動メモリを確保しない

物理メモリが 64MB 以上の場合は、アプリケーション起動メモリとして準備する 4MB の領域が、かえってスワップを頻発してパフォーマンスの低下を引き起こす。 この起動メモリを解放して、空き物理メモリを確保するには、レジストリエディタから、[HKEY_LOCAL_MACHINE]->[SYSTEM]->[CurrentControlSet]->[Control]->[Session Manager]->[Memory Management] を開き、LargeSystemCache の値を 1 に設定する。

カーネルを常に物理メモリ上に置く

「窓の手」を使った方が楽です。

標準では、カーネルのうちページプール領域が HDD に確保されるので、これもメモリ上に展開する場合は、レジストリエディタで [HKEY_LOCAL_MACHINE]->[SYSTEM]->[CurrentControlSet]->[Control]->[Session Manager]->[Memory Managemenr] というキーを開き、DisablePagingExecutive という値を 1 にする。

[レジストリ関係を纏めたファイル]

Borland C++Builder 5 Professional

OnMouseOver イベントが欲しい

オブジェクト指向のプログラミングスタイルで便利なのは、ボタンを押したり離したりするイベント毎にコードを分割できることで、ユーザーの視点からプログラミングできることです。 Borland C++ Builder は Microsoft の Visual C++ よりもこの点を非常に意識した作りになっていて、イベント以外のプログラミングを殆ど不要にしています。

ただ、一つ不便なのはどのコントロールにも OnMouseOver イベントがないこと。 同様に OnMouseOut イベントもありません。HTML で簡単に記述できるイベントがさくっと記述できないのはちょっと痛い…ので、必要に応じてイベントを拡張します。

たとえば TImage でマウスの Over/Out に対応して画像を切り替えたい…とかいうとき、コンポーネントの追加で、TImage から以下のように派生します。

/* TMImage.hpp */
class PACKAGE TMImage : public TImage
{
  private:
    void __fastcall CMMouseOverEvent(TMessage&);
    void __fastcall CMMouseOutEvent(TMessage&);

  protected:
    TNotifyEvent FOnMouseOut;
    TNotifyEvent FOnMouseOver;

    BEGIN_MESSAGE_MAP
        VCL_MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseOverEvent)
        VCL_MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage, CMMouseOutEvent)
    END_MESSAGE_MAP(TImage)

  public:
    __fastcall TMImage(TComponent* Owner);

  __published:
    __property TNotifyEvent OnMouseOver
          = { read=FOnMouseOver, write=FOnMouseOver };
    __property TNotifyEvent OnMouseOut
          = { read=FOnMouseOut, write=FOnMouseOut };
};
/* TMImage.cpp */
//---------------------------------------------------------------------------
__fastcall TMImage::TMImage(TComponent* Owner)
{
    FOnMouseOver = NULL;
    FOnMouseOut = NULL;
}
//---------------------------------------------------------------------------
void __fastcall TMImage::CMMouseOverEvent(TMessage&)
{
    if (FOnMouseOver)
        FOnMouseOver(this);
}
//---------------------------------------------------------------------------
void __fastcall TMImage::CMMouseOutEvent(TMessage&)
{
    if (FOnMouseOut)
        FOnMouseOut(this);
}
//---------------------------------------------------------------------------

なお、上記コードでは、コンポーネントを派生する際に自動的に追加されるコードは省略してあります。

ちょっと手を加えるだけで十分便利になるのに、なぜ VCL のコンポーネントにはこれらのイベントが無いのでしょうねぇ。 …もしや OS 依存?

[追記]

なんか得意がっていたら、すでに [builder:19134] Re: コントロールの状態を変えるには。 こういう話になっていたようです。3年も前だ…。ということは、(Win2000ではないし) OS依存ではなさそうですねぇ。

まぁなぜか検索エンジンに C++Builder ML の過去ログは引っかからないので、許してくださいまし>見田@NICさん

[さらに追記]

どうもマウスのキャプチャが行われると反応できないらしいので、対策を考え中。

  1. Window Control として派生し直す
  2. タイマーを利用しつつマウスを監視

…どちらにしても面倒なことになったなぁ…

Beepに音階を付ける

Beep(int _hertz_, int _millisec_)関数で可能です。たとえば PC-9801 っぽい起動音を出すには以下のようなコードを使います。まぁネタでしか使えないような気がしますが…

Beep(1940, 80);
Beep(970, 80);

ちなみに、周波数も時間も経験的に求めたものです。周波数(第1引数)は 1760〜1980Hz とその半分くらいでしょう。時間(第2引数)を 80 くらいにすると PC-9801VX の音、200 くらいにすると PC-9821(初代98Mate) の音っぽくなります。

利用できる画像フォーマットを増やしたい

TPicture は拡張可能で、様々なフォーマットに対応させることができます。

TNGImage, TGIFImage を利用すれば、PNG, MNG, 各種JPEG, GIF などに対応できるようになります。 以下、時間ないしリクエストがあれば書きます。 個人的には、現在 TFitsImage を作成中。これを利用すれば FITS 画像を読み込むことができます。

ListView のカラム幅の自動調節

ListView ではカラムを自動的に調節してくれませんが、表示するデータをすべて追加し終わった時点で

for (int i=0; iColumns->Count; i++){
    ListView1->Column[i]->Width = -1;
}

とすれば、カラム幅がテキストにあわせて調節されます。また、Width = -2 では、カラム幅がカラムのヘッダにあわせて調節されます。

それなりの時間を消費するので、ListView の OnChange イベントなどで実装するのはお薦めできません。アイテムの追加が一通り終わった後に、こっそり調節してしまいましょう。

RPMを作る

RPM でパッケージ管理をすると、アンインストールが楽になる。自分の欲しい機能を付けることができる (これ重要。特に海外製のパッケージでは日本語機能をはじめとする国際化部分が省略されることが多い)。依存関係が面倒なので、腕に覚えがあるなら自分でパッケージを作ると楽かもしれない。

というわけで、自分で SPEC ファイルを書きます。

RPMの作り方 (概論)

MakingRPMが役に立つ。

Courier-IMAP など、ものによっては rpm -ta hogehoge.tar.gz で RPMS が作れるものもある。

wassy's SPEC file (for RedHat)

Emacs 21.2 + leim 21.2

Apel, SEMI, FLIM, Wanderlust

RPM の作り方

RPMを作るには、以下の手順を追います。

  1. 上記の *.spec ファイルを SPEC ディレクトリに保存
  2. SOURCES ディレクトリに、ソース (*.tar.gz) を保存
  3. SPEC ディレクトリで rpm -ba hoge.spec
  4. RPMS/i386 ディレクトリに hoge-%{ver}.%{rel}.i386.rpm が作成される
  5. SRPMS ディレクトリには hoge-%{ver}.%{rel}.src.rpm が作成