テスト・ファースト・プログラミングで何度も取り上げられている問題の1つは、プログラムの多くの部分が自動的にテストできないように見える点です。特に、外部リソースやライブラリーを頻繁に利用するプログラムでは、そのような外部リソースへの接続をシミュレートする適切な方法がないため、テストが困難であるように思われます。
そのようなプログラムをJavaコードのみでテストするのは難しいかもしれませんが、この問題に対処するある種のプログラミングがあります (開発ツールを使用)。それが、コンポーネント・ベース・プログラミングです。
コンポーネント・ベース・プログラミングとはどういう意味でしょうか。コンポーネント・ベース・プログラミングとは、JavaBeansや類似したテクノロジーのような実行時「コンポーネント」ではなく、単に、プログラム配布の単位に基づくプログラミングのことです。
概念上、これらの配布の単位は、Javaパッケージとおおまかに対応しています。ただし、Java言語でのパッケージは、個々に分離できないという点で非常に制限されています。各パッケージのクラスは、インポートするパッケージにコーディング的に関係付けられています (各クラスは、パッケージを明示的に参照しなければならないためです)。
各パッケージは他のパッケージと分離されていないので、あるプログラムで行われているパッケージ参照を、同じ機能を提供する他のパッケージ参照に一律に置き換えるのは困難です。
また、別々の開発チームが、偶然重複するパッケージ名を使用するかもしれません。これは、両チームが互いのパッケージを使用する場合にトラブルとなります。パッケージ名を明確に区別するために、Sunは、「各開発チームでは、開発するすべてのパッケージの接頭部にチームのインターネット・アドレスを逆にしたものを使用する」という規則を強く勧めています。この規則は多くの場合守られていますが、いつもではありません。
さらに、パッケージの命名規則が完全に守られたとしても、プログラマーが個々のコンポーネントの分離を望む理由は他にもあります。1つには、そのほうがはるかに効果的にコンポーネントをテストできるからです。これについては、コンポーネント・ベース・プログラミングのためのツールであるJiazziコンポーネント・システムを例にとって、説明しましょう。
Jiazzi: Java言語のためのコンポーネント・システム
Jiazziは、Java言語の世界におけるコンポーネント・ベース・プログラミング実現の有力候補であり、JVMとの完全な互換性とコンポーネントの完全な分離を提供しようとするものです。このJiazziは、ユタ大学のコンピューター・サイエンス学部の開発プロジェクトです。このシステムにより、プログラマーは、コンポーネントを重ね合わせ、既存のJavaコードの一番上にそれらをまとめて接続できます。Java言語やJVMの変更は必要ありません。
Jiazziの開発者は、これについて以下のように説明しています。
... Javaで、大規模なバイナリー・コンポーネントの構築を可能に (そのサポートを追加)するシステム ...。Jiazziコンポーネントは、外部のリンクと分割コンパイルに対するサポートが追加された、Javaパッケージの一般化と考えることができます。Jiazziコンポーネントは、標準のJavaソース・コードから構築されているので実用的です。Jiazziでは、Java言語への拡張も、コンポーネント内部を参照するJavaソース・コードを作成するための特別な規則も必要ありません。Jiazziは、循環的なコンポーネントのリンクとミックスイン (mixin) をサポートするので、コンポーネントは表現力が豊かです。循環的なコンポーネントのリンクとミックスインは、既存のクラスに対して新たな機能のモジュラーな追加を可能にするオープン・クラス・パターンで一緒に使用されます。
現在のJiazziの実装は、リンカー (コンポーネントの操作用) とスタブ・ジェネレーター (Jiazziを通常のJavaソース・コンパイラーと使用できるようにする) を使用してJavaプラットフォームに統合されます。JiazziのコンポーネントはJavaクラスを含み、それらをインポート、およびエクスポートします。また、継承に対するJavaプラットフォームの言語面でのサポートが、コンポーネントの境界を越えて使用できます。表現力が豊かであることに加え、コンポーネントは堅固であり、コンポーネントの実装とリンクは個別に型検査できます。
Jiazziがどのようにコンポーネントを分離するかを知るために、GUIライブラリー・パッケージを利用したJavaパッケージの簡単な例であるview について考えてみましょう。このパッケージをtoolkit と呼びます。パッケージのすべてのクラスを参照するために、パッケージのソース・ファイルの一番上にインポート・ステートメントを置きます。
package view;
import toolkit.*;
...
|
通常、これによりview パッケージがtoolkit パッケージと結びつけられます。しかし、今仮に実際にインポートするパッケージを確定せずに、このソース・ファイルをコンパイルしたいとします。toolkit パッケージとview パッケージの間の接続をコード上で記述する代わりに、全てのパッケージで有効な次のような関数を定義するという構想が可能です。それは、toolkit パッケージという情報を入手すると、それに依存するview パッケージという情報を戻す関数です。Jiazziでは、こうした関数をユニットと呼びます。
ユニットはLEGOブロックのようなもので、結合して1つのプログラムを作成することができます。ユニットを関数と考えると、Jiazziは関数合成を提供すると言えるでしょう。各ユニットは、指定された「パッケージ・シグネチャー」と共に1つ以上のパッケージを取り込み、再び、指定されたシグネチャーと共に1つ以上のパッケージをエクスポートします。
パッケージ・シグネチャーは、型のようなもので、パッケージの形状を制限します。パッケージ・シグネチャーは、パッケージで要求されるクラスや、そうしたクラスのメソッド・シグネチャーなどを定義します。エクスポートされるパッケージのシグネチャーは、インポートされるパッケージに依存する場合があります。
ユニットの型には2種類あります。
- アトムは、パッケージ間のシンプルなマッピングです。
- コンパウンドは、他のユニットの合成です。
アトムは、直接インポートおよびエクスポートするパッケージを記述します。コンパウンドは、合成するユニットでインポートおよびエクスポートされるパッケージを継承します。ユニットをLEGOブロックと考えると、アトムは個々のLEGOブロックであり、コンパウンドは、複数のLEGOブロックから構築される構造体です。
ユニットは、特別な仕様言語によって別々のファイルに記述されます。この言語は、ユニットによって入出力されるパッケージに名前を割り当てます。たとえば、以下のシンプルなアトミック・ユニットは、toolkit パッケージを取り込み、view パッケージを出力します。
atom app_view {
import toolkit: toolkit_s;
export view: view_s;
}
|
このユニットの名前はapp_view です。このユニットは、インポートするパッケージに「toolkit」という名前を割り当てます。このパッケージは、toolkit_s と呼ばれる特定のパッケージ・シグネチャーに適合するように宣言されています。ユニットによってエクスポートされたパッケージはview と呼ばれ、view_s シグネチャーに適合するように宣言されています。
前述したように、パッケージ・シグネチャーは型のようなもので、ユニットに渡される (あるいは、ユニットから戻される) 引数の形状を制限します。たとえば、toolkit_s シグネチャーは、以下のように、javax.swing パッケージのクラスのセットによく似たクラスのセットを指定することがあります。
signature toolkit_s {
class Frame {
public Container getContentPane();
public Component getGlassPane();
...
}
class OptionPane {
public Object getDialog();
...
}
...
}
|
view_s シグネチャーも、以下のように指定できます。
signature view_s {
class EditorPane {...}
class InteractionsPane {...}
...
}
|
当然ですが、シグネチャーで参照されるクラスのいくつかは、別のパッケージにある場合があります。特定のパッケージからパッケージ・シグネチャーを分離できるようにするために、Jiazziでは、パッケージ・シグネチャーのパラメーター化ができるようになっています。シグネチャーのパッケージ・パラメーターは、シグネチャー名の後に、不等号括弧で囲んで指定します。たとえば、以下のように、awt パッケージによってtoolkit_s シグネチャーをパラメーター化することができます。
signature toolkit_s<awt> {
class Frame {
public awt.Container getContentPane();
public awt.Component getGlassPane();
...
}
class OptionPane {
public Object getDialog();
...
}
...
}
|
この場合、新しいシグネチャーを考え (awt_s と呼びましょう)、awt パッケージを取り込むためにapp_view ツールキットを変更し、それによってtoolkit_s シグネチャーをインスタンス化しなければなりません。
signature toolkit_s<awt> {
class Frame {
public awt.Container getContentPane();
public awt.Component getGlassPane();
...
}
class OptionPane {
public Object getDialog();
...
}
...
}
|
この場合、新しいシグネチャーを考え (awt_s と呼びましょう)、awt パッケージを取り込むためにapp_view ツールキットを変更し、それによってtoolkit_s シグネチャーをインスタンス化しなければなりません。
atom app_view {
import my_toolkit: toolkit_s<awt_s>;
my_awt: awt_s; export my_view: view_s;
}
|
そうすると、my_toolkit とmy_view という割り当てられた名前が、あたかも本物のパッケージ名のように、Javaソース・ファイルによって参照できるようになります。実際、Jiazziによって、上述のソース・ファイルを再コンパイルし、まったく変更することなく、これらの割り当てられた名前を参照できるようになります。(それがどのように行われるかについては、後で説明します。)
LEGOブロックと同様に、それぞれのユニットは、コンパウンド・ユニットを使用して結合することができます。コンパウンド・ユニットは、特別な「link」節で他のユニットを構成し、あるユニットによってエクスポートされたクラスと、他のユニットによってインポートされたクラスを識別します。たとえば、app_view アトムは、以下のように、default_toolkit ユニットとdefault_awt ユニットによって構成できます。
atom default_toolkit {
import my_awt: awt_s;
export my_toolkit: toolkit_s<my_awt>;
}
atom default_awt {
export my_awt: awt_s;
}
compound app {
export my_view: view_s;
local v: app_view, a: default_awt, t: default_toolkit;
link
a@my_awt to t@my_awt, a@my_awt to v@my_awt,
t@my_toolkit to v@my_toolkit,
v@my_view to my_view;
}
|
app ユニットのlocal 節に注意してください。この節は、ローカル変数を定義して、「ユニット・インスタンス」を表します。これらのユニット・インスタンス内のクラスが、実際にリンクされる要素になります。ユニットを直接リンクするのではなく、ユニット・インスタンスをリンクすることにより、Jiazziは、コンパウンド・ユニットのリンケージが、構成ユニットの定義に影響しないことを明確にします。
プログラムでこうしたシグネチャー、アトム、コンパウンドを指定することにより、プログラム内のパッケージがどのようにリンクされるかが記述されます。Javaソース・ファイルは、ユニットによって入出力されたパッケージを参照しますが、そうでなければ、単なる普通のJavaファイルのように見えます。次のセクションでは、Jiazziを使用して、ユニットで指定する方法で、実際にJavaクラスをどのようにリンクできるかを説明します。
Jiazziは、3段階にわたってコードのコンパイルを行います。
- 最初に、シグネチャーのセットとユニットの定義が、Jiazziスタブ・ジェネレーターに渡されます。Jiazziスタブ・ジェネレーターは、渡されたすべてのユニットのシグネチャーでインポートされたすべてのクラスに対してスタブ・クラス・ファイルを生成します。
- 次に、これらのクラス・ファイルが、従来のJavaコンパイラーによって使用され、スタブ・ジェネレーターに渡されたユニットでエクスポートされているクラスのソース・ファイルをコンパイルします。
- ソース・ファイルのコンパイル後、Jiazziユニット・リンカーは、結果のクラス・ファイルが、元のユニットで宣言されたクラス・シグネチャーと一致することを確認します。これは以下の理由で必要です。
- あらゆるサード・パーティー製のコンパイラーがJiazziサポート付で使用できる
- JiazziはJavaソース・ファイルを調べない
(余談ですが、Javaソース・コードを実際に調べないJiazziのアプローチが、思わぬ幸運を招いたことに注目してください。これにより、Jiazziは、JythonやJSR-14、NextGenなど、JVMを対象とした非Java言語用のコンパイラーと一緒に使用することができます。実際に、Jiazzi自体JSR-14で作成されています。)
確認後、コンポーネント・リンカーは、渡された各ユニットに対してJARファイルを生成します。このJARには、コンパイル済みのソースとスタブ、シグネチャー情報がメタデータとして含まれています。その後、これらのJARは、Jiazziと適切なコンパウンド・ユニットに渡すことにより、リンクすることができます。
Jiazziユニット・リンカーは、オフラインで、クラス・ファイルの定数プールに対してのみ作業を行い、メソッド名の偶発的な衝突を回避するために、隠しメソッドの名前を変更します。
ユニットは、特別なクラス・ローダーによってオンラインでリンクすることもできます。ただし、そのユニットが、その対象としてコンパイルされたスタブ・クラスは利用できないので、型検査は、「インクリメンタルな全プログラム分析」としてクラス・ローダーで行わなければなりません。実際に、現在、標準のJavaライブラリーの多くのクラスが、クラス・ローダーを介さなければリンクできないので、Jiazziプログラマーは、オフラインとオンラインのリンクを組み合わせて使用しなければなりません。
現在のシステムのもう1つの制限は、名前の変更が、JNIとリフレクション・ライブラリーに悪い影響を与えるという点です。特に、ネイティブ・メソッドはCコードで作成されているため、名前の変更が行われません。その結果、クラス・ライブラリーの多く (JNIとリフレクションに大きく依存している) が、Jiazziコンポーネントとしてリパッケージできません。
すでに述べたように、コンパウンド・ユニットは、他のユニット間の接続を表します。このリンケージは、コンパウンドのインポートおよびエクスポートされたユニットのバウンド名に関係していますが、リンクされたユニットは、リンクが行われたことをまったく認識しません (リンカーは、最終的にこれらのコンパウンド・ユニットをマクロ展開してアトムにします)。
このようにして、簡単な再コンパイルによって新しいパッケージをスワップ・インおよびスワップ・アウトできる方法で、プログラムのJARファイルを作成し、配布することができます。Javaソース・コードは一行も変更する必要はないのです。
さらに、Jiazziの他のユーザーは、私たちのプログラムによって提供されるクラスを、そのJARファイルが利用できるようになる前に開発することができます。必要なのは、エクスポートするものに対応するパッケージ・シグネチャーのみです。また、それらの拡張も、まったく同じようにそのパッケージ・シグネチャーの他の実装にリンクします。
コンポーネント・ベース・プログラミングは、大きな利点をもたらします。最も多く取り上げられる利点は、コンポーネントがコードの再利用の可能性を広げるということです。既製のコンポーネントを個々に分散し、新しいアプリケーションに自由に接続することができます。ただし、この種のプログラミングは、はるかに有効なユニット・テストも可能にします。
テストの間に、プログラムの構成コンポーネントは、クラスがテスト対象のコンポーネントのアクションを記録するだけの特別な「模擬」コンポーネントとリンクすることができます。本質的に、そのような模擬コンポーネントは、Recorderのコンポーネントと同様の役割 (参考文献の「正しいメソッド呼び出しのためのRecorderテスト」を参照) をコンポーネント・レベルで行います。
テスト済みのコンポーネントは、デカルトの「培養槽の中の脳」のようなもので、模擬コンポーネントに接続されているのか本物のコンポーネントに接続されているのか区別できません。たとえば、前述のアプリケーションの例では、以下のように、app_view ユニットとtest_toolkit ユニットを接続する特別なコンパウンドtest_app を作成できます。
compound test_app {
export my_view: view_s;
local v: app_view, a: default_awt, t: test_toolkit;
link
a@my_awt to t@my_awt, a@my_awt to v@my_awt,
t@my_toolkit to v@my_toolkit,
v@my_view ot my_view;
}
|
test_toolkit パッケージは、toolkit と同じシグネチャーのパッケージをエクスポートしますが、このパッケージは、app_view に本物のツールキットにリンクされているように思わせる模擬オブジェクトのみで構成されています。これらの模擬オブジェクトは、app_view によって実行されたアクションを記録でき、その記録内容はユニット・テストで確認できます。
このようなシステムと、各ソース・ファイルでインポート対象のパッケージを明示的に示さなければならないJavaパッケージ・システムを比較してみましょう。Javaパッケージ・システムにおいて、パッケージ全体をテスト・パッケージにリンクさせる唯一の方法は、ソース・ファイルをすべて編集し、再コンパイルすることです。しかし、この方法は、実際のところテストの自動化には有効ではありません。
残念ながら、Jiazziは、リフレクションやJNIを (あまり適切に) 処理できず、また、このような機能は、多くの組み込みJava APIによって広く使用されているため、既存のAPIパッケージをJiazziコンポーネントに変換することはできません。しかし、それができれば、プログラムに対して非常に強力なテストを実行することができるでしょう。たとえば、実際にグラフィックス・オブジェクトをロードせずに適切なAPI呼び出しのすべてが行われるように、テストの実行時、Swing APIを使用するプログラムを模擬Swingコンポーネントに接続できるようになるかもしれません。同様に、java.io パッケージやJava3D、JDBC、RMI、および機能のパフォーマンスや性質によりクライアントがテストを行うのが難しいあらゆるAPIとの相互作用をテストできるようになるかもしれません。
いかがですか。すばらしい夢でしょう? しかし、リフレクションとJNIによって、既存のAPIを優れたJiazziユニットにできない場合でも、妥協の余地があります。
既存のAPI間の接続は分離できませんが、新しいユニットからそうしたAPIへの接続の分離は可能でしょう。本質的に、このようなAPIは、クラスのエクスポートのみを行った特別なユニットでバンドルすることができ、そのインポートは、既存のJavaパッケージ・システムをそのままを使って行われます。こうしたAPIがまっとうに作られたユニットであるならば、それを行うことにより、私たちが持ち得る機能の99 %が現実のものとなるでしょう。喜ばないプログラマーは、既存のAPIに対してサード・パーティーの代替の構築を望んでいる人たちだけでしょう。
Jiazziが、このような方向、あるいは、既存のAPIとの使用を可能にする同様な方向へと進化することが望まれますが、当面、Jiazziは、リフレクションやJNIを使用しないテスト・パッケージに非常に強力なメカニズムを提供します。
Jiazziの背後にある本来の動機は、機能の拡張性のためのメカニズムとして、設計を複数の機能 (各機能を別々のコンポーネントに置く) に分割することでした。コンポーネント・ベース・プログラミングの提唱者は、開発者が、コンポーネント・ベンダーによって提供された既製のコンポーネントからプログラムを構築できるような将来を心に描いています。共通のシグネチャーを持つコンポーネントを車の部品のようにスワップ・インおよびスワップ・アウトすることができます。
実現してもしなくても、また、遍在的なコンポーネント・ソフトウェア市場がなくても、コンポーネント・ベース・プログラミングには大きな利点があります。とりわけ、この種のプログラミングは、非常に効率的なユニット・テストを可能にします。
これまでに説明してきたように、Jiazziによるコンポーネント・ベース・プログラミングは、プログラム・コンポーネントをテストするための非常に強力な方法を提供し、既存のJava (またはJython、またはJSR-14) ソース・ファイルと一緒に使用されます。私たちはみな、この強力なツールの恩恵を受けます。このようなツールや、それと同様のその他のツールは、テスト指向のプログラミングに必要不可欠な部分です。
次回は、Jamについて取り上げます。これは、ミックスイン・ベースのプログラミングを可能にするJava言語の拡張です。Jiazziがパッケージの依存関係を分離する方法を提供するように、ミックスインは、クラスの依存関係を分離する方法を提供します。お気づきかもしれませんが、ミックスインは、プログラムをテストするためのもう1つの強力なメカニズムを提供します。
- 無料のJiazzi システムに関する情報クリアリングハウスには、2ページからなる要約 (PDF)、
Jiazzi出版物のリスト、ダウンロード・サイト、ユーザー・マニュアルとチュートリアル (PDF) があります。
-
XPサイトには、エクストリーム・プログラミングの背景にある考え方がまとめられています。
- 人気のあるRoy Miller氏によるdeveloperWorks のコラム「エクストリーム・プログラミングの神秘を解く 」は、XPについて事実だけを述べ、その利点を知るのに役立ちます。
- 「XPの真髄」に立ち戻る 第1回: XPの誇大宣伝を詳しく検討する
- 「XPの真髄」に立ち戻る 第2回: プログラマーのプラクティスを全体像に当てはめる
- 「XPの真髄」に立ち戻る 第3回: 顧客役と管理役のプラクティス
- Temporal Roverの試用版 (実行時の時相論理アサーションをチェックするためのツール) がTime-RoverのWebサイトにあります。
-
JUnitのWebサイトには、プログラム・テスト手法を論じているさまざまな文献に掲載されている数多くの興味深い記事へのリンクが示されています。
-
「テスト可能な」アプリケーションの設計」 (2001年9月) や「正しいメソッド呼び出しのためのRecorderによるテスト」 (2001年6月) など、Ericの 「Javaコードの診断」 のコラムのすべての記事をお読みください。
- バグ・パターン: Javaプログラムで頻発しがちなバグの分析と修正
- 「宙ぶらりん複合型」バグ・パターン: ヌル・ポインター例外の最もよくある原因を鎮圧する
- 「ヌル・フラグ」バグ・パターン: 例外状況を表すフラグとしてヌル・ポインターを使うことを避ける
- 「二段たどり」バグ・パターン: 再帰的なクラス・キャストという概念上のエラーを最初から克服する
- うそつきビューのバグ・パターン: GUIの最良の友になってうそつきビューを暴き出しましょう
- 破壊工作データのバグ・パターン: 隠れたデータ爆弾が奇妙なクラッシュの原因かもしれません
- 破綻したディスパッチのバグ・パターン: 引き数のアップ・キャストによって、不正確なメソッドの呼び出しを修正する
- Javaコードのパフォーマンスを向上させる: 末尾再帰変換はアプリケーションの速度を向上させる可能性はあるが、すべてのJVMで可能な操作ではない
- 正しいメソッド呼び出しのためのRecorderによるテスト: メソッドの呼び出しを順序正しく行うために、ユニット・テストのためのRecorderを記述する
- 型詐欺師のバグ・パターン: タグを使用したオブジェクトの型の区別は、ラベルの貼り違えにつながる可能性がある
- クリーンアップ・コード散在バグ・パターン: リソースの獲得および解放を同時に実行する
- 虚偽の実装というバグ・パターン: 第1回: 前提とした不変条件がインターフェースの破壊を招くこともある
- 虚偽の実装というバグ・パターン: 第2回: 表明とユニット・テスト - バグを除去するための実行可能なドキュメンテーション -
- 「みなし子スレッド」バグ・パターン: マスター・スレッドが自滅し、その他のスレッドが生き残っていると、どうなるか?
- 「テスト可能な」アプリケーションの設計: 以下に示す7つの原則は、テストを念頭においてコード設計を行う際のもとになるものです
- 拡張可能アプリケーションの設計 第1回: ブラック・ボックス、オープン・ボックス、またはガラス・ボックス: どんな場合にどれがふさわしいか?
- 拡張可能アプリケーションの設計 第2回: ガラス・ボックスはどんな時、どこで、どのように最もよく機能するかを調べる
- 拡張可能アプリケーションの設計 第3回: ブラック・ボックスはどんなとき、どこで、どのように最もよく機能するかを調べる
- 拡張可能アプリケーションの設計 第4回: S式がどのように軽量のブラック・ボックス拡張性を実現するか
- 深さ優先visitorと、破綻したディスパッチ: このVisitorパターンの変形を使えば、コードをより簡潔にできます
- 仕様という綱渡り: 明確に定義された仕様が、ソフトウェア・システムにとってなぜ重要か
- replによる対話式評価: ソフトウェアを効率的かつ対話式に診断するためのテクニックとツール
- 「付け足し初期化コード」バグ・パターン: 引数の足りないコンストラクターを避ければ、このバグを撃退できる
- プラットフォーム依存を引き起こす"犯人": プラットフォーム依存のバグ・パターンにスポットライトを当てる
- 静的型の場合: 好きでも嫌いでも、静的型チェックでコードをもっと堅固にできる
- Javaプログラミングにおけるアサーションと時相論理: テストを補足するものとしてアサーションに時相論理を取り入れる
- 時相論理をバグ・パターンに適用する: 時相論理アサーションによる、一般的なバグの防止
- 単体テストと自動コード分析の連携: テストを工夫して、ツールによるコード分析を手助けする
- developerWorksJava technologyゾーンでJavaテクノロジーに関する数多くの記事やチュートリアルをご覧ください。
Eric Allen氏は、コーネル大学でコンピューター・サイエンスと数学の学士号を取得しています。現在は、ライス大学の博士課程の大学院生としてJavaプログラミング言語チームに加わっています。学位を終了するためにライス大学に戻るまでは、Cycorp, IncでJavaソフトウェア開発主任として勤務していました。彼は、JavaWorldで「Java Beginner」ディスカッション・フォーラムの司会者も務めています。主な研究対象は、Java言語のセマンティック・モデルと静的分析ツールの開発であり、いずれもソース・レベルとバイトコード・レベルで研究しています。Ericは、NextGenというプログラミング言語 (汎用ランタイム型によるJava言語の拡張版) のためのライスのコンパイラーの開発にも携わってきました。連絡先は、eallen@cs.rice.edu です。