Android標準ホームアプリ(Launcher2)のパッケージを変更してビルドしてみた

備忘録エントリ。
Android標準ホームアプリ(Launcher2)のパッケージを変更してビルドしてみたので手順をメモしておく。環境は、Mac OS X 10.7(Lion)、Android 3.2。

1.SDKのビルド

http://d.hatena.ne.jp/learn/20120111/p1
の手順でSDKのビルドを行う。

2.アプリの変更

単純に、ホームアプリをビルドするだけであれば、

. build/envsetup.sh
cd packages/apps/Launcher2
mm

で、out/target/product/generic/system/app/Launcher2.apkが出力される。
ここでは、標準ホームアプリと別アプリ扱いになるよう、packages/apps/Launcher2をコピーしてpackages/apps/MyLauncherとする。
packages/apps/MyLauncher以下のファイルについて、以下の変更を行う。

  • ソースパッケージの変更(com.android.launcher2 -> jp.example.mylauncher)
  • AndroidManifest.xmlの変更
    • を変更(com.android.launcher -> jp.example.mylauncher)
    • android:sharedUserIdを削除
    • タグを削除
    • のandroid:nameのパッケージを変更(com.android.launcher2.Xxx -> .Xxx)
    • を変更(com.android.launcher2.settings -> jp.example.mylauncher.settings)
    • ※com.android.launcher.permission.xxxのものは変更不要。
  • res配下のxml内のパッケージ記述の変更(com.android.launcher2 -> jp.example.mylauncher)
  • proguard.flagsのパッケージ記述の変更(com.android.launcher2 -> jp.example.mylauncher)
  • Android.mkのLOCAL_PACKAGE_NAMEの変更(Launcher2 -> MyLauncher)

3.バージョン設定ファイルの確認

build/core/version_defaults.mkのPLATFORM_VERSION_CODENAMEを確認する。
AOSP(Android Open Source Project)になっている場合、実行環境もAOSP版でないとインストールできない。標準のエミュレータ等へインストールする場合は、以下のように変更する。

PLATFORM_VERSION_CODENAME := REL

4.ビルド、インストール

. build/envsetup.sh
cd packages/apps/MyLauncher
mm

で、out/target/product/generic/system/app/MyLauncher.apkが出力される。

adb install out/target/product/generic/system/app/MyLauncher.apk

でインストールされる。インストール後、端末でHOMEキーを押せば、アプリ選択ダイアログが表示される。
他にも変更必要なものもあるかもしれないが、とりあえず、これで起動はできた。

Android 3.2 SDK をMac(OS X 10.7 Lion)でビルドする手順

備忘録エントリ。
Android 3.2のSDKをLionでビルドしたので手順をメモしておく。
基本的には、公式ドキュメントの以下の手順に従えばよい。
手順ではSnow Leopard使えとか、MacPorts使うようになっているが、その辺りは変えている。
http://source.android.com/source/initializing.html
http://source.android.com/source/downloading.html
http://source.android.com/source/building.html

1.Xcode 3のインストール

Mac Dev CenterよりXcode 3をダウンロードする。無料だがアカウント登録が必要。
取得したバージョンは、xcode_3.2.6_and_ios_sdk_4.3だった。
最新版のXcode 4はApp Storeにあるが、Xcode 4の環境の場合、make時に以下のようなエラーでビルドに失敗してしまうためXcode 3を使用する。*1

Please install the 10.5 SDK on this machine at /Developer/SDKs/MacOSX10.5.sdk
external/qemu/Makefile.android:81: *** Aborting the build..  Stop.

Xcode 3のインストールだが、Xcode 3をダウンロードしてそのままFinderからmpkgを実行しても、Lionの場合、Xcode Toolsetがスキップになってしまいインストールできないので、以下のようにする必要がある。

export COMMAND_LINE_INSTALL=1
open /Volumes/Xcode\ and\ iOS\ SDK/Xcode\ and\ iOS\ SDK.mpkg

インストーラが起動するので、インストールの種類で
iOS SDK以外がすべてチェックされている状態にして*2インストールする。

2.ディスクイメージの作成、マウント

大文字小文字を区別するファイルシステムが必要になるため、ディスクイメージを作成する。
アプリケーション > ユーティリティ > ディスクユーティリティを起動し、新規イメージを作成する。
内容は以下の通りとする。

名前 android(任意)
場所 ホームディレクトリ(任意)
ボリューム名 android(任意)
ボリュームサイズ 公式ドキュメントの記載は25GB以上だが、Android 3.2のSDKのビルドのみであれば20GB以上で足りた。
ボリュームフォーマット Mac OS拡張(大文字/小文字を区別、ジャーナリング)
暗号化 なし
パーティション 単一パーティション - Appleパーティションマップ
イメージフォーマット 読み出し/書き込みディスクイメージ

作成後、マウントする。

sudo hdiutil attach ~/android.dmg  -mountpoint /Volumes/android

3.gitのインストール

http://code.google.com/p/git-osx-installer/downloads/list?can=3
からインストーラをダウンロードして、インストールする。

4.Homebrewのインストール

公式ドキュメントでは、MacPortsが使われているが、今はHomebrewの方が流行っていると思うので、Homebrewを使用した。インストール方法は以下の通り。

ruby -e "$(curl -fsSLk https://gist.github.com/raw/323731/install_homebrew.rb)"

5.gnupgのインストール

brew install gnupg

6.repoのインストール

mkdir ~/bin
PATH=~/bin:$PATH
curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
chmod a+x ~/bin/repo

7.オープンファイルディスクリプタの上限変更

vi ~/.bash_profile

以下を設定する。

ulimit -S -n 1024

読み込み。

. ~/.bash_profile

8.ソースコードの取得

cd /Volumes/android
mkdir src
cd src
repo init -u https://android.googlesource.com/platform/manifest -b master -m base-for-3.2-gpl.xml

※最新のバージョンを取得する場合は、

repo init -u https://android.googlesource.com/platform/manifest

とする。特定のバージョンを取得する場合は

repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1

のようにタグを指定する。*3
ただし、Honeycombのみマニフェストファイルが異なるため、上記のように、-mでマニフェストファイルを指定する必要がある。
なお、マニフェストファイルは/Volumes/android/src/.repo/manifests/に格納される。

repo init実行後は、対象バージョンのソースコードの取得を行う。

repo sync

大量(10GB以上)にダウンロードされるため、回線速度によっては非常に時間がかかる。

9.SDKのビルド

. build/envsetup.sh
lunch sdk-eng
make -j4 sdk

※4はマシンのCPUコア/スレッド数に合わせる。


ビルドが終わると、以下にSDKのZIPアーカイブが出力されている。
/Volumes/android/src/out/host/darwin-x86/sdk/android-sdk_eng.ユーザ名_mac-x86.zip

なお、各種SDKのjarファイルは、/Volumes/android/src/out/target/common/obj/JAVA_LIBRARIES 以下に出力されている。

*1:Xcode 4を先にインストールしていたため、Xcode 3と4のインストール先ディレクトリを、それぞれ/Developer3と/Developerとして共存させた上で、/Developer3/SDKs/MacOSX10.4u.sdkと/Developer3/SDKs/MacOSX10.5.sdkを/Developerへシンボリックリンクをはり、さらにexport CC=/Developer3/usr/bin/llvm-gcc-4.2としてビルドを試みたが、同じエラーになったため、諦めてXcode 3と4をアンインストール後、Xcode 3のみを再インストールした。

*2:iOS SDKが不要だったため。iOS関連の開発を行う場合はiOS SDKにもチェックすればよい。

*3:2012/1/15 追記。-b android-sdk-4.0.3_r1で取得したソースコードを上記と同じ手順でビルドしようとしてもエラーになってmakeできませんでした。

外部からAndroidエミュレータへTCPまたはUDPで通信する方法

備忘録エントリ。
Androidエミュレータは内部的に仮想ルータが動作することにより、ホストマシン(エミュレータが動作しているマシン)のネットワークとは別に、10.0.2.0/24のネットワークで動作している。このため、外部からエミュレータへの通信をするにはちょっとした準備が必要になる。

方法は、http://developer.android.com/intl/ja/guide/developing/devices/emulator.html#redirections(日本語訳はSign in - Google Accounts)に記載されているが、要はホストマシンのポートで受けた通信をエミュレータのポートへ転送させるようにすることで対応する。
手順は以下の通り。

1.エミュレータを起動
2.telnetlocalhostエミュレータコンソールポート(1つ目のエミュレータなら5554)へ接続
3.エミュレータコンソールで以下のコマンドを実行

redir add tcpまたはudp:ホストマシンのポート番号:エミュレータのポート番号

例.ホストマシンのTCP/5000への通信をエミュレータTCP/6000へ転送する場合

redir add tcp:5000:6000

確認

redir list
tcp:5000 => 6000と出力される。

設定すると、ホストマシン上で0.0.0.0に対して、ホストマシンのポート番号に指定したポート(上記例では5000)が開き、待ち受け状態になるので、通信したいマシンからホストマシンのそのポートへ通信してやればエミュレータの指定ポートへ転送される。
なお、転送をやめる場合は以下のようにする。

redir del tcp:5000

4.実際に通信して確認してみる
なぜか、http://developer.android.com/intl/ja/guide/developing/devices/emulator.htmlにも載ってないのだが、
エミュレータコンソールで、パケットキャプチャが可能なのでそれで確認してみる。
キャプチャの開始は以下のコマンド。

network capture start ホストマシンのキャプチャファイル出力先フルパス

例.ホストマシンの/tmp/hoge.cap(WindowsならC:\tmp\hoge.cap)へ出力する場合

network capture start /tmp/hoge.cap

で、telnetなりnetcatなりでホストマシン向けに通信する。

nc ホストマシンのIPアドレス 5000

キャプチャの停止は以下のようにする。

network capture stop

あとは、出力されたファイルをWiresharktcpdumpで見てみると、10.0.2.2(ホストのループバックアドレスエイリアス) -> 10.0.2.15(エミュレータIPアドレス)へ通信が行われているのが確認できる。

なお、ポート転送の設定はエミュレータコンソールだけでなく、adbコマンドでも可能。

adb forward tcp:ホストマシンのポート番号 tcp:エミュレータのポート番号

この場合、エミュレータだけでなく実機でも設定可能。

64bit Windows 上で 32bit アプリケーションから 64bit アプリケーションを実行する方法

64bit版のWindowsでは、32bitアプリケーションを動作させるために、ファイルシステムリダイレクタという仕組みが使用されている。詳細は、File System Redirector - Windows applications | Microsoft Docs 等に記述されているが、簡単にいうと、以下のような仕組みになっている。
通常、アプリケーションがDLL等を読みに行く先は %windir%\System32\ 以下だが、ここには64bit用のものが置かれている。64bitアプリケーションであればこれで問題ないが、32bitアプリケーションの場合は32bit用のファイルを読む必要がある。しかし、ほとんどのDLLは32bitと64bitで同一の名前になっているため、32bit用のファイルは%windir%\SysWOW64\ 以下に置かれている。つまり、アプリケーションが32bitか64bitかで同一のDLLを読む場合でも見に行く先を変える必要がある。これをOSで制御して32bitの場合は透過的に%windir%\SysWOW64 を見せているのがファイルシステムリダイレクタ。
しかし、場合によってはこのリダイレクタが問題になることがある。
例えば、32bitアプリケーションから64bitアプリケーションを実行したい場合。
私が経験した例では、JP1/AJSから、batファイルを起動し、そこからPowerShellを起動し、PowerShellスクリプト内でOracle Clientを使用してOracle Databaseへアクセスする場合に困ったことがあった。JP1/AJSは32bitで、Oracle Clientは64bitアプリケーション。batファイルにはPowerShellのパスとしてC:\Windows\System32\WindowsPowerShell\v1.0\powershell.exeが記述されている。この状況で普通にJP1/AJSからジョブを実行すると、32bitのOracle ClientのDLLを読もうとして見つからずにエラーになってしまう。ファイルシステムリダイレクタにより、実際には、32bit版のC:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exeが実行されてしまうためだ。そういう場合は、以下のようなパスを指定することで、ファイルシステムリダイレクタを働かせずに64bit版のファイルを指定できる。
C:\Windows\sysnative\WindowsPowerShell\v1.0\powershell.exe
ちょっと面倒なのは、64bitアプリケーション上からC:\Windows\sysnative\を指定しても使用できないこと。このため、C:\Windows\sysnative\WindowsPowerShell\v1.0\powershell.exeへ記述を修正したbatファイルをエクスプローラ等から直接実行してもパスが見つからずエラーになってしまう。C:\Windows\SysWOW64\cmd.exe 等32bitアプリケーションから実行する必要がある。試してはいないが、JP1/AJSからbatを実行する際に、C:\Windows\sysnative\cmd.exe を指定すればよいはず。

apacheで複数のバーチャルホストでSSLキーのパスフレーズが異なる場合のSSLPassPhraseDialogの書き方

備忘録エントリ。
apache起動時にSSL秘密鍵パスフレーズを自動入力させるには、SSLPassPhraseDialog ディレクティブを使えばいいが、複数のSSLのバーチャルホストをたてて、かつそれぞれの秘密鍵パスフレーズが異なる場合、SSLPassPhraseDialog はバーチャルホストコンテキストには書けないのでちょっと困った。mod_ssl - Apache HTTP Server Version 2.2 を見ると、SSLPassPhraseDialog で指定する外部プログラムに「サーバー名:ポート番号」と「RSA」または「DSA」の2つの引数が渡されるようになっている模様。なので、以下のようにすれば対応できる。
SSLPassPhraseDialog は通常通りサーバーコンフィグコンテキストに記述する。
例.(2011/1/28 追記。渡される引数がServerNameに由来することがわかるよう例を修正しました。)

SSLPassPhraseDialog exec:/usr/local/apache/bin/pp_filter
...
<VirtualHost 203.0.113.101:443>
ServerName www1.example.jp
...
SSLCertificateKeyFile /etc/apache/certs/server1.key
...
</VirtualHost>
<VirtualHost 203.0.113.102:8443>
ServerName www2.example.jp:8443
...
SSLCertificateKeyFile /etc/apache/certs/server2.key
...
</VirtualHost>

/usr/local/apache/bin/pp_filter

#!/bin/sh

case $1 in
    www1.example.jp:443)
        /bin/echo "server1.keyのパスフレーズ"
        ;;
    www2.example.jp:8443)
        /bin/echo "server2.keyのパスフレーズ"
        ;;
esac
exit 0
蛇足

セキュリティ上あまり好ましくはないかもしれないが、以下のように秘密鍵からパスフレーズを削除することもできる。

mv server.key server.key.backup
openssl rsa -in server.key.backup > server.key
最後に、、、

エントリ内容とは全然関係ないですが、MacBook Air 11インチ欲しい!

TomcatでHTTPメソッドを制限する設定方法

忘れがちになるので、自分用にメモ。
TomcatでHTTPメソッドを制限する設定方法は以下の通り。

$CATALINA_HOME/conf/web.xml に以下の設定を入れる。には拒否したいメソッドを列挙する。下記例はすべてのURLについてGETとPOST以外を拒否する場合の設定。

<security-constraint>
    <web-resource-collection>
        <web-resource-name>hoge</web-resource-name>
        <url-pattern>/*</url-pattern>
        <http-method>HEAD</http-method>
        <http-method>PUT</http-method>
        <http-method>DELETE</http-method>
        <http-method>OPTIONS</http-method>
        <http-method>TRACE</http-method>
        <http-method>CONNECT</http-method>
        <http-method>PATCH</http-method>
        <http-method>PROPFIND</http-method>
        <http-method>PROPPATCH</http-method>
        <http-method>MKCOL</http-method>
        <http-method>COPY</http-method>
        <http-method>MOVE</http-method>
        <http-method>LOCK</http-method>
        <http-method>UNLOCK</http-method>
    </web-resource-collection>
    <auth-constraint/>
</security-constraint>

上記設定により拒否された場合は403 Forbiddenが返されるようになる。

なお、Tomcat 6.0.29で試したところデフォルトで許可されている(HTTPステータスコード200が返される)メソッドはGET、HEAD、POST、OPTIONS。PUT、DELETEは403 Forbidden、TRACEは405 Method Not Allowed、CONNECT、PATCH、PROPFIND、PROPPATCH、MKCOL、COPY、MOVE、LOCK、UNLOCKは501 Not Implementedが返される。ただ、OPTIOSを実行すると、

Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS

と返されるので、PUT、DELETEも許可されているのかも。実験方法が

PUT / HTTP/1.0

というようなリクエストなのでリクエスト方法の問題かもしれない。
これらを考慮すると、上記設定もTRACE以下は不要ともいえる。
ちなみに、TRACEメソッドを有効にしたい場合は、server.xml要素にallowTrace="true"を記述すればOK。


あと、Tomcat 7.0.4で試してみると、よくわからないことになった。
TRACEで405 Method Not Allowedが返されるのは同じだが、他はなんでも200が返ってくる模様。CONNECT、PATCH、PROPFIND、PROPPATCH、MKCOL、COPY、MOVE、LOCK、UNLOCKどころか、

A / HTTP/1.0

とか

XXX / HTTP/1.0

とかやっても200が返ってくる。というわけで、Tomcat 7の場合、上記のようにブラックリスト方式では完全には制限できないのかも。

TomcatとOracle間のコネクションプーリングに関するトラブルシューティング

Tomcatでコネクションプーリングを使用していて、Tomcat <-> Oracle間の接続がFINやRSTパケットによる通知なしに切られた場合、プールしている接続が実際には死んでいる状態が発生する。例えば、以下のような場合に発生する。

  • APサーバー <-> DBサーバー間のFirewallによるセッション切断
  • DBサーバーのリブート(Windows Server 2008の場合。他は未確認)

なお、Windows Server 2008で試した限りではOracleサービスの再起動やshutdown、startupの場合は発生しなかった。おそらくFINまたはRSTパケットが飛んでいると思われる。

一度、この状態に陥ると、その後ネットワークやDBサーバーが復旧しても死んだ接続がプールに残り続け、その接続を使用したTomcatのスレッドはSQLを実行する処理でOracleからの応答をずっと待ちつづけてしまう。
特にDBCPの設定でtestOnBorrow、validationQueryによりプールからの接続取得時にSQL実行による接続の検証を行うようにしていると、検証SQL実行時に応答待ちになり、しかもvalidationQueryの実行はプールからの接続の取得処理内で行われるため(接続数の管理のために)処理が同期化されているため、他のスレッドがプールから接続を取得するのをブロックする状況になり、やがてTomcatのmaxThreadsやHTTPサーバーの接続上限に達し、応答停止状態になる。
ブロックしているスレッドのスタックトレースはこんな感じになる。(S2JDBCを使用)

"TP-Processor48" daemon prio=10 tid=0x00002aaafc638000 nid=0x6d4a runnable [0x0000000046684000]
java.lang.Thread.State: RUNNABLE
     at java.net.SocketInputStream.socketRead0(Native Method)
     at java.net.SocketInputStream.read(SocketInputStream.java:129)
     at oracle.net.ns.Packet.receive(Packet.java:240)
     at oracle.net.ns.DataPacket.receive(DataPacket.java:92)
     at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:172)
     at oracle.net.ns.NetInputStream.read(NetInputStream.java:117)
     at oracle.net.ns.NetInputStream.read(NetInputStream.java:92)
     at oracle.net.ns.NetInputStream.read(NetInputStream.java:77)
     at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1034)
     at oracle.jdbc.driver.T4CMAREngine.unmarshalSB1(T4CMAREngine.java:1010)
     at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:588)
     at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:183)
     at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:780)
     at oracle.jdbc.driver.T4CStatement.executeMaybeDescribe(T4CStatement.java:855)
     at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1178)
     at oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:1377)
     - locked <0x00002aaae90bd2e8> (a oracle.jdbc.driver.T4CConnection)
     at oracle.jdbc.driver.OracleStatementWrapper.executeQuery(OracleStatementWrapper.java:387)
     at org.apache.tomcat.dbcp.dbcp.DelegatingStatement.executeQuery(DelegatingStatement.java:208)
     at org.apache.tomcat.dbcp.dbcp.DelegatingStatement.executeQuery(DelegatingStatement.java:208)
     at org.apache.tomcat.dbcp.dbcp.PoolableConnectionFactory.validateConnection(PoolableConnectionFactory.java:658)
     at org.apache.tomcat.dbcp.dbcp.PoolableConnectionFactory.validateObject(PoolableConnectionFactory.java:635)
     at org.apache.tomcat.dbcp.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1165)
     at org.apache.tomcat.dbcp.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106)
     at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
     at org.seasar.extension.dbcp.impl.DataSourceXADataSource.getXAConnection(DataSourceXADataSource.java:80)
     at org.seasar.extension.dbcp.impl.ConnectionPoolImpl.createConnection(ConnectionPoolImpl.java:430)
     at org.seasar.extension.dbcp.impl.ConnectionPoolImpl.checkOut(ConnectionPoolImpl.java:350)
     - locked <0x00002aaaee21a4e8> (a org.seasar.extension.dbcp.impl.ConnectionPoolImpl)

netstatでいうと、APサーバー側でESTABLISHEDのTCPセッションが、DBサーバー側には存在しない状態になる。

この問題を解決すべく色々調査したのでまとめておく。
なお、調査で色々試した環境はTomcat 6.0.29@Red Hat Enterprise Linux 5、Oracle 11gR1@Windows Server 2008R2。

実現したいこと

DBサーバーやネットワーク障害発生時に、復旧次第、自動的にTomcat <-> Oracle間の接続が復旧するようにしたい。
そのために、validationQueryのタイムアウト時間は短く設定し、接続が切断されていることをOSのTCP/IP KeepAliveタイムアウト時間まで待たずにもっとすぐに検知してタイムアウトさせたい。
タイムアウトさせることでDBCPでその接続を破棄して再接続させる(testOnBorrowの機能)ことで、DB側やネットワークが復旧次第Tomcat <-> Oracle間の接続も復旧させることが可能。
一方で、アプリケーションのSQLが処理中にタイムアウトさせられてしまうことは避けたい。

調査したこと

1.Statement#setQueryTimeout()

JDBCではSQLタイムアウトにはこれを使うのがおそらく標準だが、Oracle JDBC Driverでは今回のような場面ではこれは効かない。これはOracle JDBC Driverのクエリタイムアウトの仕組みが、単純にタイムアウト時間を超えたらエラーを返すものではなく、タイムアウト時間を超えたらOracleサーバーにクエリキャンセル要求を行い、その結果をもってタイムアウトエラーをクライアントに返すものだから。*1今回のように接続が死んでいてキャンセル要求の応答も返ってこない場合は、タイムアウトせず待ち続けてしまう。
なお、調査の過程で見つけたのだが、DBCPには現時点ではDBCP – BasicDataSource Configurationには記載されていないvalidationQueryTimeoutというvalidationQueryのタイムアウト時間を設定する隠し?パラメータがあるのだが、これも実装はStatement#setQueryTimeout()を実行しているだけなので今回は使えない。
もちろん、Oracle JDBC Driverでも、今回のように接続が切れてしまっている場面でなければStatement#setQueryTimeout()は効く。

2.Oracle JDBC Driverの接続プロパティ

上述のOracleドキュメントに記載されている、oracle.net.READ_TIMEOUTを試した。だが、1000(1秒)を設定して2分以上経過してもタイムアウトしない。おそらく効いていない。インターネットで色々調べていると、よく似たoracle.jdbc.ReadTimeoutというのを見つけた。実験してみると1000(1秒)を設定して約65秒でタイムアウトした。これにoracle.net.CONNECT_TIMEOUTというのを併用することで両方1秒設定で約6秒でタイムアウトさせることができた。微妙にずれがあるのは、おそらくDBCPがリトライしたりしている分の時間かな?(未確認)
また、上記設定をしていると、DBサーバー、ネットワークが復旧すれば接続もすぐに復旧することが確認できた。
設定方法。JDBC接続設定に以下を記述する。

connectionProperties="oracle.jdbc.ReadTimeout=ミリ秒;oracle.net.CONNECT_TIMEOUT=ミリ秒"
3.SQLNET.EXPIRE_TIME

上記2の設定だけだと、validationQueryだけではなく単純に処理に時間のかかるクエリもタイムアウトしてしまう。これを回避する方法について調査しているとSQLNET.EXPIRE_TIMEというsqlnet.oraのパラメータを見つけた。sqlnet.oraファイルのパラメータによると、Oracleサーバー側のSQLNET.EXPIRE_TIMEに0より大きい値を設定することで、Oracleサーバーからクライアントに対して接続されている接続が生きているか確認するためのプローブパケットを送信し応答がなければ接続を閉じるような動きをする模様。タイムアウト時間内にプローブパケットを送信させることで、タイムアウトを防げるのではないかと考えた。一定時間Sleepするストアドを作成して実際に試したところ、上記2の設定時間を超えてもタイムアウトせずに正常にクエリ実行ができることが確認できた。
ただし、SQLNET.EXPIRE_TIMEを設定すると、Oracleがプローブパケットを送信するかわりにTCP/IP KeepAliveプローブパケットが送信されなくなるという情報をみつけた。*2また、SQLNET.EXPIRE_TIMEのプローブパケット送信は、ステータスがKILLEDのOracleセッションは対象外になるらしい。これらが真実だとすると、上記設定を行うことで、例えば手動でセッションをKILLした場合、Oracleをshutdownしない限り、TCP/IP KeepAliveタイムアウト時間を過ぎてもKILLEDセッションが残り続けてしまうことになるのでは?と心配になり、ALTER SYSTEM KILL SESSIONで実験したところ、30分程度かかったがKILLEDセッションがなくなることを確認した。必ずなくなるのか他に条件があるのかは不明だが、少なくとも必ず残り続けるわけではないことはわかった。また、最悪OS側からプロセスをkillすることも可能。WindowsOracleはスレッドで動いているのでOS標準機能で特定のスレッドだけkillするのはおそらくできない?が、OTN掲示*3の書き込みによると、WindowsOracleにはorakillというコマンドが入っていて、SPIDを指定してkillすることが可能らしい。SPIDはv$processにあるので、v$sessionと結合してステータスがKILLEDのものを検索すればよいはず。
SQLNET.EXPIRE_TIMEの設定方法。サーバーのORACLE_HOME/network/admin/sqlnet.oraに以下を記述する。

SQLNET.EXPIRE_TIME=プローブパケットの送信間隔(分)

まとめ

上記2と3により、実現したいことが達成できた。実際に2と3の設定を行った状態でTomcat <-> Oracle間の通信をDROPしたり、DBサーバーを再起動しても、ネットワークやDBが復旧すると自動的にTomcatからのDB接続が復旧し、Webアプリケーションも動作することが確認できた。
最終的に設定は以下のようにする予定。
Tomcat

connectionProperties="oracle.jdbc.ReadTimeout=130000;oracle.net.CONNECT_TIMEOUT=130000"

Oracle

SQLNET.EXPIRE_TIME=1
補足

上記2はThin Driverを使う場合の設定。OCI Driverを使う場合は、実験した限りでは以下の設定で同様のことが可能。上記2の設定もおそらく同等のものと思われる。
APサーバー側のsqlnet.oraに以下の設定を記述する。

SQLNET.OUTBOUND_CONNECT_TIMEOUT=クライアントがDBサーバーへのOracle Net接続を確立する秒数
SQLNET.RECV_TIMEOUT=接続が確立した後にクライアントがDBサーバーからの応答データを待機する秒数
2010/11/11 追記

Tomcatへのアクセス数が少なかったり、ピークは多くても普段は少ないというような場合には、DBコネクションプールのアイドル接続数(maxIdleやminIdle)を小さめにするよう考慮する必要がある。
理由は、プールしている接続が多いとその分だけ復旧に時間がかかるため。上記で「タイムアウトさせることでDBCPでその接続を破棄して再接続させる(testOnBorrowの機能)」と簡単に書いたが、実際には、ValidationQueryでエラーになった(タイムアウトした)場合は、その接続を破棄して別のプールしている接続を取得する。ここまでの挙動はDBCPの公式ドキュメントに記載されている通り。で、その先どうなるのかDBCPのコードを確認してみると、その別のプールしている接続にもValidationQueryを実行し、エラーになった場合は破棄して、さらに別のプールしている接続を取得し、、、ということを繰り返し、最終的にプールしている接続がなくなれば新規に接続し直すという実装になっている。
今回のようにプールしている接続がすべて死んでいる場合、それぞれのValidationQuery実行時に、設定したタイムアウト時間がかかるので、復旧までには、単純計算で、プールしている接続数 x タイムアウト時間の時間がかかってしまう。これはアクセスが1回しかない場合の話で、例えばプールしている接続数分の同時アクセスがくれば、プールしていた接続はすべて同時に検証され同時にタイムアウトして空きがなくなるのですぐに復旧する。上記でアクセス数が少ない場合と書いたのはこのため。
DBCPの設定でtestWhileIdleをtrueにすることで、timeBetweenEvictionRunsMillisの設定間隔で、numTestsPerEvictionRunの数だけ接続の検証が行われるので、これを併用するともう少し復旧を早めることができるはず。ただし、numTestsPerEvictionRunをプール接続数と同じにすればすぐに復旧させられるかと思いきや、実際に試してみるとnumTestsPerEvictionRunに少ない値を指定したときと違いが感じられなかった。DBCPのコードを確認したわけではないが、おそらくtestWhileIdleによる検証はnumTestsPerEvictionRunの数だけ「1つずつ」順次実行しているのではないか。それであれば今回の目的のためには、タイムアウト値よりtimeBetweenEvictionRunsMillisが長くない限り、2以上を設定しても効果はないと思われる。