EclipseとHSQLDB: リレーショナル・データベース・サーバーをEclipseに埋め込む 第1回

HSQLDBデータベース・サーバーをEclipseワークベンチに統合するプラグインをどう書くか

この記事ではPure Javaのリレーショナル・データベース・サーバーであるHSQLDBをEclipseワークベンチに埋め込むプラグインを、どのように開発するかを説明します。HSQLDB(hypersonic SQL database)はDB2ほど強力でもなく、MySQLほど普及もしてはいませんが、拡張性が高く、メモリやプロセッサーに対する要求も厳しくないことから、広範囲のJavaアプリケーションでの必要要件を満たすものです。

Fernando Lozano (fernando at lozano.eti.br), Independent consultant, IBM

Fernando Lozanoは長年オープンソースの熱心な信奉者であり、Java開発者であり、Java and GNU/Linux(ポルトガル語版のみ)の著者でもあります。またEclipse他のオープンソース・ツールを使ってJ2EEアプリケーションを開発している組織に対してサービスを販売しており、GNUプロジェクトWebサイトのポルトガル語翻訳の維持管理を補助し、またLinux Professional Institute Brazil評議会のメンバーでもあります。連絡先はfernando at lozano.eti.brです。



2003年 9月 30日

hypersonic SQL(正式名は後にHSQLDBと改められました)はPure Javaのリレーショナル・データベース・サーバーであり、(ダイレクト・ファイル・アクセスを使って)スタンドアローン・モードで使うこともできればクライアント/サーバー・モードでも使用でき、また同時に多くのユーザーを受け付けることができるものです。HSQLDBはDB2ほど強力でもなく、MySQLほど普及もしてはいませんが、拡張性が高く、メモリやプロセッサーに対する要求も厳しくないことから、広範囲なJavaアプリケーションでの必要要件を満たすものです。

HSQLDBは便利なJava開発データベースで、SQL(Structured Query Language 構造化クエリー言語)のサブセットが豊富です。また開発用のワークステーションにプロセッサーやメモリを追加したり、巨大なディスク容量を要求するデータベース・サーバーをインストールしたりする必要もありません。初心者にも経験豊富な開発者にも使える便利なツールがあるので、Eclipse IDEに統合するには理想的なツールと言えます。

今回の記事と今後のシリーズでは、どのようにEclipseプラグインを作ればEclipseワークベンチにHSQLDBを組み込むことができるかを説明します。APIやユーザー・インターフェース(UI)を考慮した上でプラグインをどう開発するか、また、ユーザーが望むような機能を提供するために代替手段をどう考慮すべきかといった点が、現実的な例として理解できると思います。この記事の前提として、読者はプラットフォーム・ランタイム・バイナリとJDTの組み合わせではなく、Eclipse SDKディストリビューションを使っているもの、とします。単に通常のJavaアプリケーションを開発することだけが目標であれば、前者が適当でしょう。

このシリーズでは「Levels of Integration」(参考文献にリンクがあります)での表現に従って、3段階でプラグインのセットを作成、拡張します。

  1. Eclipseから既存のツールを起動し、ワークベンチのメニューから必要な既存ツールに簡単にアクセスできるようにする
  2. 既存ツール・セットの価値を高め、Java開発者の生産性を高めるために、他のEclipseのフィーチャーをどのように使えばよいかを検討する
  3. Eclipseワークベンチとスムーズな統合ができるように、SWTを使ってツールを書き直す

HSQLDBを知る

HSQLDBは、ソースとドキュメンテーションを含めてSourceForge(hsqldb.sourceforge.net、リンクは参考文献)からダウンロードできます。凍結されてしまっている、元々のSourceForgeプロジェクトhsql.sourceforge.netとは違いますので間違えないようにしてください。

バイナリのディストリビューションは標準のZIPファイルなので、ハードドライブのどこかで解凍するだけです。すべてのHSQLDBコンポーネント(つまりデータベース・エンジン、サーバー・プロセス、JDBCドライバー、ドキュメンテーション、そして一握りのユーティリティ等)は一つのJARパッケージに入っており、約260 KBでlib/hsqldb.jarにインストールされます。データベース・エンジンを実行するには170 KBのRAMしか必要ないので、シャープのザウルスのようなPDAでも問題ありません。ソースやドキュメンテーションを含めて全部をダウンロードしても標準の1.44 MBフロッピーに充分収まります。

コマンド・プロンプトからorg.hsqldb.Serverorg.hsqldb.util.DatabaseManagerのようなコンビニエンス・クラスを呼び出すと、データベース・サーバーとユーティリティが起動します。コンビニエンス・クラスには -url(リモート・コネクション)や-database(ダイレクト・ファイル・アクセス)、-userといった、ちょっとしたオプションもあります。また -? オプションを使うと、有効なコマンドライン構文のヘルプを表示することもできます。

HSQLDBが単純なのはSQLステートメント実行をシリアル化しているためです。つまり、多くのユーザーが同時にデータベースに接続できるのですが(サーバー・モードの時)、すべてのSQLステートメントはキューに置かれ、一度に一つだけしか実行されません。ですから複雑なロックや同期アルゴリズムを実装する必要はありません。にもかかわらず、HSQLBはACID(Atomicity, Consistency, Isolation, and Durability)を備えています。言い換えればHSQLBはトランザクショナル・データベースですが、トランザクション・アイソレーション無しのread uncommittedのレベルでしかありません。HSQLDBは本来、組み込みアプリケーション用に作られており、企業のデータセンター用ではないのです。

トリガーや収集関数、他との結合、ビューその他のSQLのフィーチャーが欲しければ、HSQLDBにはどれも備わっているのです(大部分の軽量のリレーショナル・データベースには普通こうした機能はありません)。ストアード・プロシージャーはJavaクラスを追加してHSQLDBクラスパスを行わせることで実装します。次にCREATE FUNCTIONステートメントを発行すればそれで終わりです。実際、SQRTABSといった標準SQL機能の多くが、java.lang.Mathのような標準Javaクラスに直接マッピングすることで実装されているのです。

HSQLDBの操作モード

HSQLDBエンジンはいろいろなアプリケーションのパターンに合うように、多くの実行モードが用意されています。

メモリ内モード
データベース・テーブルとインデックスはすべてメモリに配置され、ディスクに保存されることはありません。アプリケーションの終了と共にデータベースも失われてしまって良いのかと尋ねる前に、データベースのローカル・キャッシュがあることを考えてみてください。このローカル・キャッシュで、標準SQLステートメントを使ったクエリーもソートも、グループ化、更新もできるのです。

スタンドアローン・モード
アプリケーションがJDBCを使ってデータベース・コネクションを生成し、アプリケーション内でHSQLDBエンジンが実行してデータベース・ファイルを直接アクセスします。複数ユーザーが同時アクセスすることはできません(アプリケーションがデータベース・ファイルに排他的アクセス権があります)が、追加スレッドやTCPコネクションのオーバーヘッドがありません。多くの埋め込みアプリケーションではスタンドアローン・モードがよく使われます。

サーバー・モード
これは他のリレーショナル・データベースに似た、標準のクライアント/サーバー型データベース構成で、TCPソケットを使っての同時接続が可能です。メインのアプリケーションが実行中であっても、どのJDBCクライアントもテーブルに接続してクエリーや更新ができるので、多くの開発者にはこのモードが必要でしょう。

Webサーバー・モード
HSQLDBはWebサーバーとしても動作します。HTTPでSQLクエリーを受け付けることができ、どんな標準のWebコンテナ内部でもサーブレットとして実行でき、ファイアーウォールを通過し、またプロバイダーのサポート・チームの助け無しにWebホスティング・サービスにインストールすることができます。HTTPはステートレスなので、このモードではトランザクションがありません。

HSQLDBデータベースのファイル構造

HSQLDBはすべてのテーブルとインデックス・データをメモリに配置します。発行されたすべてのSQLステートメントは database.scriptと呼ばれるファイルに保存されますが、これはトランザクションのログとしても動作します。エンジンが初期化されると、このファイルが読み込まれ、すべてのSQLステートメントが処理され、完全なデータベースが再構築されます。シャットダウンの際には、データベースが高速に立ち上がるように、HSQLDBエンジンが最小限のステートメント・セットで新しい database.scriptファイルを生成します。

デフォルトのメモリ・テーブルとは別にHSQLDBは「キャッシュ」テーブルと「テキスト」テーブルもサポートします。キャッシュされたテーブルはすべてdatabase.dataと呼ばれるファイルに保存され、テキスト・テーブルは任意の区切りテキストファイル形式(例えばCSV)で保存されますが、そのファイル名をつけるのはset table sourceという非標準のSQLステートメントです。キャッシュ・テーブルの方は使用可能なRAMよりも大きなデータセットをサポートし、テキスト・テーブルの方はデータのインポートやエクスポートに便利です。

database .scriptとdatabase.dataとファイルとは別に、HSQLDBデータベースがdatabase.propertiesファイルを持っている場合もあります。管理者はこのファイルに対して、ANSI SQLとの互換性に影響するような各種のパラメーターを設定することができます。すべてのデータベース・ファイルは(テキスト・テーブル・データファイルを除いて)、同じディレクトリに保存する必要があります。

HSQLDBデータベースを明示的に作成する方法はありません。エンジンに対して、(サーバー・モードの-databaseオプション、またはスタンドアローン・モードのJDBC URLのいずれかを使って)存在していないデータベース・ファイルを開くように命令すると、ファイルと、それを含むディレクトリが生成されます。ですから、その空のデータベースの中に確かにデータがあったはずだと思うのであれば、タイプをしそこなったのかもしれません。

ではプラグインの開発を始めましょう!


HSQLDBのEclipseプラグインセットを作る

既存のアプリケーションをEclipseのような強力なツールの内部に置くのは簡単な作業ではありません。幸いHSQLDBもEclipseも、その作業は楽なのです。これはHSQLDBが他のアプリケーションに埋め込めること、またEclipseにはきれいで理解しやすいプラグイン基礎構造と、新しいプラグインであるPDEを作るための堅牢な開発環境があるからです。ですからEclipseプラグイン開発に経験が無くても、PDEのおかげで作業が非常に簡単になるのです。この記事の最後に参考文献としてEclipseの基本を解説した記事を挙げておきますので、参考にしてください。

ここではプラットフォーム・ランタイム・バイナリとJDTの組み合わせではなく、EclipseのSDKディストリビューションを使っている、という前提で解説します。単に通常のJavaアプリケーションを開発することだけが目標であれば前者が適当でしょう。ネイティブコードは全く使わず、Javaコードのみしか使わないので、好みのオペレーティング・システムを使うことができます。

まず最小限の作業でできることとして、最小限のコードを使って便利なプラグインのセットを作ります。このシリーズの後の方で、どのようにEclipseのフィーチャーを活用すればHSQLDBがより価値あるものになるかを説明します。

この記事では、次のような機能を持つコードに取り組みます。

  1. HSQLDBエンジンをサーバー・モードで起動し、ユーザー・アプリケーションとSQLコンソール(例えばHSQLDB自体のDatabaseManagerユーティリティ)が同時にSQLステートメントを実行できるようにする。
  2. HSQLDBデータベースをきれいに終了する。
  3. DatabaseManagerユーティリティを呼び出し、開発者がワークベンチからインタラクティブにSQLステートメントを入力できるようにする。
  4. HSQLDBのScriptToolユーティリティを使ってSQLスクリプト・ファイルを実行する。長年私は自分のプロジェクト・フォルダーに *.sqlファイルをいくつも保存してきました。データベース・テーブルを生成したりテスト・データを挿入したりするためですが、こうしたファイルを簡単に実行する方法がないか長年待っていたのです。
  5. HSQLDBコネクション・プロパティ(TCPポートや管理パスワード)を設定する。

どうすればワークベンチでこうした機能が使えるようになるのでしょうか。最初の3つを実現する一番簡単な方法は、新しい、最上位レベルのメニューにactionSetを追加し、かつワークベンチ自体のツールバーからも使えるようにすることです。SQLスクリプトファイルの実行は「*.sql」を拡張子とするファイルのみが対象なので、これはobjectContributionです。これをワークベンチが、対象ファイルを表示するどのビューのポップアップ・メニューにも追加します。最後に、このコネクション・パラメーターはプラグイン・プリファレンス・ページにうまく収まり、ワークベンチのWindowメニューから操作できます。

図1は新しいメニューとツールバー、図2はNavigatorビューのポップアップ・メニューに新しいエントリーが入ったところ、図3はプロパティ・ページです。これで私たちのプラグイン・セットの第一リリースがどんな風に見えるか、ちらっとは分かってもらえると思います。

図1. HSQLDBメニューと関連のツールバー
図1. HSQLDBメニューと関連のツールバー
図2. *.sqlに追加されたポップアップ・メニュー
図2. *.sqlに追加されたポップアップ・メニュー
図3. HSQLDBコネクション・プロパティ
図3. HSQLDBコネクション・プロパティ

HSQLDBをEclipseプラグインに変える

ここでのプラグイン・セットを作る上での第一歩はHSQLDB自体をEclipseプラグインとしてパッケージしてしまうことです。このEclipseプラグインが含むのはhsqldb.jarと、ワークベンチが要求するplugin.xmlファイルのみです。標準のEclipseプラグインのディレクトリをちょっと見たことがあれば、JUnitやXercesそれにTomcat等、よくあるJavaパッケージはそれぞれのプラグインの中に変更無く分離されており、Eclipse特有のものはすべて他のプラグインにカプセル化されていることが分かったでしょう。サードパーティ製のツールの更新は必ずしもEclipse自体の変更を必要としないので、この区分によってそうした更新が容易になります。多くのプラグインとライブラリーを共有できることも別の利点の一つです。

Eclipse SDKを開き、新しいプラグイン・プロジェクトを作り、名前をhsqldb.coreとします。(推奨の名前はorg.hsqldb.coreだということは知っていますが、私はHSQLDBにネームスペースは付けません。これを読んでいるHSQLDB開発者はこの考え方を気に入って、「正式の」Eclipse統合プラグインとして推薦してくれるかも知れません。その場合には多分、名前は変更されるでしょう。)必ず「Empty Plugin」オプションを選び、他のプラグイン・テンプレートを選ばないようにしてください。そうしないと使い道のない最高レベル・プラグイン・クラスができてしまいます。もし作ってしまって気になるようなら、安全に削除することもできます。HSQLDBからプロジェクト・ディレクトリにhsqldb.jarをコピーし、プロジェクトのRuntimeに追加します。これにはPDE Plugin Manifest Editorを使うこともできますし、単純にリスト1からコピーすることもできます。

リスト1. hsqldb.core プラグインのplugin.xmlマニフェスト・ファイル
<?xml version="1.0" encoding="UTF-8"?>
<plugin
id="hsqldb.core"
name="Hsqldb Core Plug-in"
version="0.0.1"
provider-name="Fernando Lozano (www.lozano.eti.br)">
<runtime>
<library name="hsqldb.jar">
<export name="*"/>
</library>
</runtime>
</plugin>

ワークベンチに拡張機能を追加する

次にhsqldb.uiという名前で2番目のプラグイン・プロジェクトを作ります。これは最初のプラグイン・セットにある、Eclipseのすべての拡張機能を含みます。つまり、3つのアクションを含むアクション・セット、*.sqlファイルに関連付けられたオブジェクト・コントリビューション、それにプロパティ・ページを含んでいます。メイン・クラス(Pluginクラス)の名前をPluginUiとし、デフォルトのパッケージ名hsqldb.uiを受け入れます。

Plugin Manifest Editorでplugin.xmlファイルを開き、Extensionsタブを選択します。コントリビュートされたメニューをHSQLDBに変更し、ラベル「Runs HSQLDB Database Manager」を表示するようにサンプル・アクションを変更します。同じactionSetに2つのアクションを追加し、それぞれ「Stops HSQLDB database server」、「Starts HSQLDB database server」というラベルを付け、固有のアクションIDと実装クラスを与えます。こうしたアクションは、作ったのとは逆の順番(プラグイン・マニフェスト・ファイルに現れるのと逆の順番)で、メニュー・バーやツールバーに表示されることに注意してください。

Addボタンをクリックし、Extensionテンプレートとポップアップ・メニュー、プロパティ・ページを使って2つの新しい拡張機能を追加します。ポップアップ・メニューは*.sqlファイルのパターンと関連付けられているはずですが、プロパティ・ページのフィールドはプログラム的に設定されます。

図4はすべてのプラグイン拡張機能を表示し、Plugin Manifestエディターが最終的にどう見えるかを示しています。図5はオブジェクト・コントリビューションのプロパティ(*.sqlファイル用のフィルタに注意してください)を、図6はファイル・リソースのポップアップ・メニューに対する「Run SQL Script」アクションのプロパティを示しています。アイコンはこの記事のソースコード(参考文献)の中にありますが、読者ならもっとカッコ良いアイコンを描けるグラフィックスのエキスパートを知っているでしょう。

図4. マニフェスト・エディターでのHSQSLDBアクション
図4. マニフェスト・エディターでのHSQSLDBアクション
図5. マニフェスト・エディターでのHSQLDBオブジェクト・コントリビューション
図5. マニフェスト・エディターでのHSQLDBオブジェクト・コントリビューション
図6. マニフェスト・エディターでSQL Scriptアクションを実行する
図6. マニフェスト・エディターでSQL Scriptアクションを実行する

我々のアクションにコードを追加する前に、対応するコア・プラグインについてUIプラグインに伝える必要があります。そうしないとこのUIプラグインがHSQLDBにアクセスできません。Plugin Manifest Editorの「Dependencies」ページに変更し、図7に示すように、Plugin Dependencyを追加します。「eclipse.ui」のように一部の依存性はPDEが自動的に設定しますが、他の「org.eclipse.debug.core」等はアクションがコード化された時に追加されます。直接XMLコードを編集したければ、リスト2がhsqldb.uiプラグインに対する完全なplugin.xmlファイルを示しています。

プラグインの設定を完了するためにhsqldb.uiパッケージに新しいClassを追加し、名前をHsqldbUtilとします。このクラスはHSQLDBと直接やり取りするすべてのコードを含んでいるので、コントリビュートされた拡張機能のコードが簡単になります。

図7. hsqldb.ui プラグイン 依存
図7. hsqldb.ui プラグイン 依存
リスト2. hsqldb.uiプラグインに対するplugin.xmlマニフェスト・ファイル
<?xml version="1.0" encoding="UTF-8"?>
<plugin
id="hsqldb.ui"
name="Hsqldb Ui Plug-in"
version="0.0.1"
provider-name="Fernando Lozano (www.lozano.eti.br)"
class="hsqldb.ui.PluginUi">
<runtime>
<library name="ui.jar"/>
</runtime>
<requires>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.ui"/>
<import plugin="hsqldb.core"/>
<import plugin="org.eclipse.debug.core"/>
<import plugin="org.eclipse.jdt.launching"/>
<import plugin="org.eclipse.debug.ui"/>
</requires>
<extension
point="org.eclipse.ui.actionSets">
<actionSet
label="Hsqldb"
visible="true"
id="hsqldb.ui.actionSet">
<menu
label="Hsql&db"
id="hsqldbMenu">
<separator
name="dbServerGroup">
</separator>
</menu>
<action
label="Run Hsql &Database Manager"
icon="icons/dbman.gif"
tooltip="Runs the Hsql database manager"
class="hsqldb.ui.actions.HsqldbDatabaseManagerAction"
menubarPath="hsqldbMenu/dbServerGroup"
toolbarPath="dbServerGroup"
id="hsqldb.ui.actions.HsqldbDatabaseManagerAction">
<enablement>
<pluginState
value="activated"
id="hsqldb.ui">
</pluginState>
</enablement>
</action>
<action
label="S&top Hsqldb"
icon="icons/stop.gif"
tooltip="Stops the Hsql database server"
class="hsqldb.ui.actions.HsqldbStopAction"
menubarPath="hsqldbMenu/dbServerGroup"
toolbarPath="dbServerGroup"
id="hsqldb.ui.actions.HsqldbStopAction">
<enablement>
<pluginState
value="activated"
id="hsqldb.ui">
</pluginState>
</enablement>
</action>
<action
label="&Start Hsqldb"
icon="icons/start.gif"
tooltip="Starts the Hsql database server"
class="hsqldb.ui.actions.HsqldbStartAction"
menubarPath="hsqldbMenu/dbServerGroup"
toolbarPath="dbServerGroup"
id="hsqldb.ui.actions.HsqldbStartAction">
<enablement>
<pluginState
value="installed"
id="hsqldb.ui">
</pluginState>
</enablement>
</action>
</actionSet>
</extension>
<extension
point="org.eclipse.ui.perspectiveExtensions">
<perspectiveExtension
targetID="org.eclipse.ui.resourcePerspective">
<actionSet
id="hsqldb.ui.actionSet">
</actionSet>
</perspectiveExtension>
</extension>
<extension
point="org.eclipse.ui.popupMenus">
<objectContribution
objectClass="org.eclipse.core.resources.IFile"
nameFilter="*.sql"
id="hsqldb.ui.SQLScriptFiles">
<action
label="Run SQL Script"
class="hsqldb.ui.popup.actions.HsqldbRunScript"
menubarPath="additions"
enablesFor="1"
id="hsqldb.ui.HsqldbRunScript">
<enablement>
<pluginState
value="activated"
id="hsqldb.ui">
</pluginState>
</enablement>
</action>
</objectContribution>
</extension>
<extension
id="hsqldb.ui.preferences"
point="org.eclipse.ui.preferencePages">
<page
name="HSQLDB Server"
class="hsqldb.ui.preferences.HSQLDBPreferencePage"
id="hsqldb.ui.preferences.HSQLDBPreferencePage">
</page>
</extension>
</plugin>

HSQLDBを起動する

クラスorg.hsqldb.Serverを使い、サーバー・モードでHSQLDBエンジンを起動するのは簡単です。コマンドラインの引数として、HSQLDBエンジンはデータベース名(パス+データベース・ファイルのベース名)、コネクション要求をリスンするためのTCPポート、シャットダウン時にSystem.exit()をコールすべきかどうかを示すフラグを受け取ります。下記はHSQLDBを実行する典型的なコマンドラインです。

java -cp /opt/hsqldb/hsqldb.jar org.hsqldb.Server -database /tmp/bd -port 9001 -system_exit=true

上のコマンドラインでデータベース・ファイル、/tmp/db.scriptと/tmp/db.properties、それに /tmp/db.dataが作られます。

Serverクラスのメイン・メソッドを使って引数とストリングの配列を渡しますが、これを新しいスレッド内部で行う必要があります。そうしないとワークベンチ全体をロックしてしまいます。プラグインクラスは単集合ですが、このスレッドへの参照を保持して、接続しようとする前に起動されたサーバーがないかどうか、またそれが実際に実行していたかどうかをチェックします。これはどんなクライアントでも、接続してSHUTDOWNステートメントを実行すればサーバーを停止できてしまうからです。

リスト3のコードはhsqldb.ui.actions.HsqldbStartActionrunメソッドを示します。リスト4はhsqldb.ui.HsqldbUtilstartHsqldbのコードを示し、これが実際にサーバーを起動します。後ほど、ユーザーからのフィードバックを操作する手法について説明します。

このコードで一番面白いのは、データベース・ファイルの保存場所の見つけ方です。エンジンが .hsqldbというプロジェクト(存在していない場合は新たに作られます)の中を検索してdatabase.scriptや他のデータベース・ファイルを見つけるのです。

Eclipseが提供するすべてのプラグインは、どの設定ファイルも保存される標準ディレクトリにあります。plugin.getStateLocationをコールすればそのディレクトリは分かりますが、データベース・ファイルは実際にプラグインの設定ファイルのようなものだとは思わないでください。データベース・ファイルはむしろユーザーのデータファイルのように見え、従って開発者のワークスペースのプロジェクト内にあるはずです。

リスト3. hsqldb.ui.actions.HsqldbStartActionから、サーバー・モードでHSQLDBを起動するアクション
public void run(IAction action) {
// check a database was really started by the plug-in
PluginUi plugin = PluginUi.getDefault();
if (plugin.getHsqldbServer() != null) {
((ApplicationWindow)window).setStatus(
"HSQLDB Server already running.");
}
else {
Cursor waitCursor = new Cursor(window.getShell().getDisplay(),
SWT.CURSOR_WAIT);
window.getShell().setCursor(waitCursor);
try {
HsqldbUtil.startHsqldb();
((ApplicationWindow)window).setStatus("HSQLDB Server started.");
}
catch (CoreException e) {
MessageDialog.openError(window.getShell(),
"Hsqldb Plugin",
"Could not create HSQLDB database project.");
e.printStackTrace(System.err);
}
finally {
window.getShell().setCursor(null);
waitCursor.dispose();
}
}
}
リスト4. hsqldb.ui.HsqldbUtilから、実際にサーバー・モードでHSQLDBを起動するコード
public static void startHsqldb() throws CoreException {
PluginUi plugin = PluginUi.getDefault();
// finds project local path for database files
IWorkspaceRoot root = PluginUi.getWorkspace().getRoot();
IProject hsqldbProject = root.getProject(".hsqldb");
if (!hsqldbProject.exists()) {
hsqldbProject.create(null);
}
hsqldbProject.open(null);
IPath dbPath = hsqldbProject.getLocation();
final String database = dbPath.toString() + "/database";
// starts a new thread to run the database server
final HsqldbParams params = getConnectionParams();
Thread server = new Thread() {
public void run() {
String[] args = { "-database", database,
"-port", String.valueOf(params.port),
"-no_system_exit", "true" };
Server.main(args);
}
};
plugin.setHsqldbServer(server);
server.start();
}

HSQLDBを停止する

HSQLDBサーバーを停止するには、SQLステートメントとしてのSHUTDOWNコマンドが必要なだけです。リスト5に示す、hsqldb.ui.HsqldbUtilstopHsqldbメソッドがまさにそれです。対応するアクションのコードは、サーバー起動用に記述したものとほとんど同じなのでここには挙げません。アクション・コードがユーザーに適切なフィードバックとなるように、例外ClassNotFoundExceptionClass.forNameの)とSQLExceptionがスローされます。

リスト5. HSQLDBを停止するコード
public static void stopHsqldb() throws ClassNotFoundException,
SQLException {
PluginUi plugin = PluginUi.getDefault();
HsqldbParams params = getConnectionParams();
// submits the SHUTDOWN statement
Class.forName("org.hsqldb.jdbcDriver");
String url = "jdbc:hsqldb:hsql://127.0.0.1:" + params.port;
Connection con = DriverManager.getConnection(url, params.user,
params.passwd);
String sql = "SHUTDOWN";
Statement stmt = con.createStatement();
stmt.executeUpdate(sql);
stmt.close();
// no need to close a dead connection!
plugin.setHsqldbServer(null);
}

HSQL Database Managerを実行する

HSQLDBからDatabase Managerユーティリティを実行するのはサーバー自体を実行するよりも少し複雑です。サーバーは他のアプリケーションに埋め込むように作られていますが、ユーティリティはスタンド・アローンのアプリケーションとして実行するか、Javaアプレットとして実行するように作られているのです。どちらのモードもコマンドラインの引数(またはアプレット・パラメーター)を受け付けますが、Windowを開いてユーザーがSQLステートメントをタイプできるようにするためだけに新たにJava VMを起動するという、余計なオーバーヘッドは追加したくはありません。クラスstatic void main(String[] args)を直接コールしても、System.exit()を呼び出してワークベンチを終了してしまうので、期待通りには動きません。

org.hsqldb.util.DatabaseManagerソースコードを眺めていると、それがJDBCコネクションで簡単にインスタンス化、初期化できることは分かるのですが、必要なメソッドがパブリックではないことが分かります。そこでHSQLDBソース・ツリーの中にorg.hsqldb.util.PatchedDatabaseManagerという名前のクラスを作り、付属のAntビルド・スクリプトを使って、このパッチを当てたDatabase Managerを含む新しいhsqldb.jarを作ります。2つのメソッド、void main()void connect(Connection con)のみ可視性をpublicに変更する必要があります。プラグインがこの2つを使い、パッチを当てたクラスをどう実行するかをリスト6に示します。

Database ManagerはAWTアプリケーション(図8)で、(Eclipse SWTのイベント・スレッドとは独立した)独自のイベント・スレッドを生成し、ワークベンチを閉じると終了しますが、ワークベンチの終了でSystem.exit()がコールされます。ワークベンチのウィンドウの層が足りないことを除けば、このユーティリティの複数インスタンスを実行しても問題ありません。これはHSQLDBプラグインの第一版としては問題ありませんが、このシリーズを終わる前にはEclipseビューとしてホストされた、SWTアプリケーションに変更する必要があります。

図8. HSQLDB Database Manager
図8. HSQLDB Database Manager
リスト6. パッチを当てたDatabase Managerユーティリティを起動する
public static void runDatabaseManager() throws ClassNotFoundException,
SQLException {
PluginUi plugin = PluginUi.getDefault();
HsqldbParams params = getConnectionParams();
// creates a connection to the internal database
String url = "jdbc:hsqldb:hsql://127.0.0.1:" + params.port;
Class.forName("org.hsqldb.jdbcDriver");
Connection con = DriverManager.getConnection(url, params.user,
params.passwd);
if (con != null) {
// needed to patch DatabaseManager so it could
// be initialized and use the supplied connection
PatchedDatabaseManager dm = new PatchedDatabaseManager();
dm.main();
dm.connect(con);
}        }

SQLスクリプトを実行する

HSQLDBスクリプト・ツールはプレーンテキスト・ファイルを読み込み、その中に含まれているSQLステートメントを、与えられたJDBC URLに対して実行します。まとまったSQLステートメントはgoコマンドで分けられ、スクリプトはprintコマンドを使ってスクリプトからメッセージを出力することができます。

他のデータベース・システムに慣れた開発者はセミコロン(;)で分けた、SQLステートメントだけを含むスクリプトを作ってしまうかも知れませんが、そうするとHSQLDBはすべてのスクリプトを一括で実行し、最後のステートメントの結果しか返しません。各ステートメントに対する結果を受け取るには、ステートメントの間にgoコマンドを入れる必要があります。

ユーザーがスクリプトの結果を見られるようにする一番簡単な方法は、ちょうどワークベンチから起動したJavaアプリケーションやAntビルド・スクリプトのように、スクリプトの結果をコンソール・ビューに入れることです。これにはJavaの起動設定を生成する必要がありますが、これを生成すると次に別のJava VMが生成されます。SQLスクリプトは短命なのでオーバーヘッドは許容範囲内とみなせます。Database Managerもそれ自体のVM内で起動すべきだと考えるのであれば、例としてリスト7に示すrunScriptToolメソッドを使うことができます。

リスト7はこの記事で一番長いリストですが、大部分は、正しいJREブートストラップ・クラス(これは明示的に設定する必要があります)と、hsqldb.coreプラグインのhsqldb.jarを含んだクラスパスの設定に関連した部分です。JDTが提供する、Eclipseと拡張機能による起動フレームワークについては、この記事の最後にある参考文献を見てください。

他のGUIツールキットに慣れた開発者にとっては、選択されたSQLスクリプト・ファイルへのパスがどうやって分かるのか明確には見えないかも知れません。IObjectActionDelegateインターフェースはリソース・ポップアップ・メニューを拡張するオブジェクト・コントリビューションに実装されていますが、runメソッドに受け取るのはアクション自体への参照のみなのです。このインターフェースは最上位レベルのメニューのアクションと同じように、選択されたリソースについての情報や制御の発生元の情報は何も含みません。リソースの参照はselectionChangedメソッドに渡され、後で使用できるようにインスタンス変数として保存される必要があります。リスト8を見てください。

リスト7. HSQLDB Script Toolを実行する
public static void runScriptTool(IFile currentScript) throws CoreException {
PluginUi plugin = PluginUi.getDefault();
// destroys any preexisting configuration and create a new one
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = manager.getLaunchConfigurationType(
IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
ILaunchConfiguration[] configurations = manager.getLaunchConfigurations(
type);
for (int i = 0; i > configurations.length; i++) {
ILaunchConfiguration config = configurations[i];
if (config.getName().equals("SQL Script")) {
config.delete();
}
}
ILaunchConfigurationWorkingCopy wc = type.newInstance(null,
"SQL Script");
// constructs a classpath from the default JRE...
IPath systemLibs = new Path(JavaRuntime.JRE_CONTAINER);
IRuntimeClasspathEntry systemLibsEntry =
JavaRuntime.newRuntimeContainerClasspathEntry(
systemLibs, IRuntimeClasspathEntry.STANDARD_CLASSES);
systemLibsEntry.setClasspathProperty(
IRuntimeClasspathEntry.BOOTSTRAP_CLASSES);
//... plus hsqldb.core plugin
IPluginRegistry registry = Platform.getPluginRegistry();
IPluginDescriptor hsqldbCore = registry.getPluginDescriptor(
"hsqldb.core");
ILibrary[] libs = hsqldbCore.getRuntimeLibraries();
String installDir = hsqldbCore.getInstallURL().toExternalForm();
URL hsqldbJar = null;
try {
hsqldbJar = Platform.asLocalURL(new URL(installDir +
libs[0].getPath()));
}
catch(Exception e) {
// ignore URL exceptions
}
IRuntimeClasspathEntry hsqldbEntry =
JavaRuntime.newArchiveRuntimeClasspathEntry(new Path(
hsqldbJar.getPath()));
hsqldbEntry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES);
// sets the launch configuration classpath
List classpath = new ArrayList();
classpath.add(systemLibsEntry.getMemento());
classpath.add(hsqldbEntry.getMemento());
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH,
classpath);
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH,
false);
// current directory should be the script container
IPath dir = currentScript.getParent().getLocation();
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
dir.toString());
// gets the path for the selected SQL script file
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
"org.hsqldb.util.ScriptTool");
// builds ScriptTool command line
HsqldbParams params = getConnectionParams();
String args = "-driver org.hsqldb.jdbcDriver " +
"-url jdbc:hsqldb:hsql: " +
"-database //127.0.0.1:" + params.port + " " +
"-user " + params.user + " " +
"-script " + currentScript.getName();
if (params.passwd.length() > 0)
args += "-password " + params.passwd + " ";
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,
args);
// saves the new config and launches it
ILaunchConfiguration config = wc.doSave();
DebugUITools.launch(config, ILaunchManager.RUN_MODE);
}
リスト8. どのようにhsqldb.ui.popup.actions.HsqldbRunScriptからSQL Scriptを実行するか
public void selectionChanged(IAction action, ISelection selection) {
currentScript = null;
if (selection != null) {
if (selection instanceof IStructuredSelection) {
IStructuredSelection ss = (IStructuredSelection)selection;
// as this action is enabled only for a single selection,
// it's enough to get the first element
Object obj = ss.getFirstElement();
if (obj instanceof IFile) {
currentScript = (IFile)obj;
}
}
}
}

HSQLDPプロパティ

プラグイン機能の大部分はそろったので、足りないのはサーバーとの接続パラメーター、つまりリスンすべきTCPポート、管理者のユーザー名とパスワードです。TCPポートは既に開発者のマシンにインストールされている他のアプリケーションと競合しないように、変更が必要かも知れません。ユーザーとパスワードはどのクライアントからでも、SQLステートメントで変更することができます。こうしたパラメーターはHsqldbParamsという名前で、C風の構造(またはパスカル風レコード)のクラスに入れることができます。これをリスト9に示します。このリストではPlugin Preferences StoreとProperty Pageがパラメーター(プリファレンス)の値をフェッチ、保存するために使う定数も宣言しています。

リスト9. HSQLDBのサーバー接続パラメーターのプリファレンス構造
package hsqldb.ui;
public class HsqldbParams {
// preference names for the plugin
public static final String P_PORT = "serverPort";
public static final String P_USER = "serverUser";
public static final String P_PASSWD = "serverPasswd";
public int port = 9001;
public String user = "sa";
public String passwd = "";
}

残念ながらPDE New Extension Wizardが生成するプリファレンス・ページ・クラスは、プラグイン・プリファレンス・ストアが使わないデフォルトのプリファレンス値の設定方法を含めて、ちょっと誤解を生じがちです。デフォルトを初期化するための正しい場所はメインのPluginクラス自体なのです。そうしないとデフォルト値となっているすべてのプリファレンスは、プリファレンス・ストアによって0かnullとして返されてしまうのです。

従って、プリファレンス・ページ・クラスはリスト10に示すようにもっと簡単になり、またメソッドinitializeDefaultPreferencesがリスト11に示すようにPluginUiクラスに追加されます。各プリファレンスのデフォルト値を定義するのはプリファレンス構造であり、プラグイン・クラスやプリファレンス・ページ・クラスではないことに注意してください。

リスト10. HSQLDBプラグインのプリファレンス・ページ
public class HSQLDBPreferencePage
extends FieldEditorPreferencePage
implements IWorkbenchPreferencePage {
public HSQLDBPreferencePage() {
super(GRID);
setPreferenceStore(PluginUi.getDefault().getPreferenceStore());
setDescription("Connection parameters for the embedded HSQLDB server");
}
public void createFieldEditors() {
addField(new IntegerFieldEditor(HsqldbParams.P_PORT, "&TCP Port:",
getFieldEditorParent()));
addField(new StringFieldEditor(HsqldbParams.P_USER,
"Administrator &User:",
getFieldEditorParent()));
addField(new StringFieldEditor(HsqldbParams.P_PASSWD,
"Administrator &Password:",
getFieldEditorParent()));
}
public void init(IWorkbench Workbench) {
}
}
リスト11. 生成されたPluginクラスから変更したメソッド
public void shutdown() throws CoreException {
// shuts down the server if running
Thread server = getHsqldbServer();
if (server != null && server.isAlive()) {
try {
HsqldbUtil.stopHsqldb();
}
catch (Exception e) {
e.printStackTrace();
}
}
super.shutdown();
}
protected void initializeDefaultPreferences(IPreferenceStore store) {
super.initializeDefaultPreferences(store);
HsqldbParams params = new HsqldbParams();
store.setDefault(HsqldbParams.P_PORT, params.port);
store.setDefault(HsqldbParams.P_USER, params.user);
store.setDefault(HsqldbParams.P_PASSWD, params.passwd);
}

ユーザーにフィードバックする

プラグイン・セットはワークベンチに対するビューもエディターも無いので、アクションに関してユーザーにフィードバックする手段は限られています。ワークベンチのウィンドウのステータス行を使うか、ユーザーが重要なイベント(大部分はエラー)を逃さないように、SWTMessageDialogを使います。

ワークベンチのアクションに対する可視性とイネーブルメントモデルはワークスペースリソースの選択に着目していますが、プラグインのアクションの大部分(HSQLDBサーバーの起動、停止とデータベース・マネージャーの起動)はリソース選択に依存せず、サーバーが実行中かどうかに依存します。(実行中かどうかは各アクションのrunメソッドが明示的にチェックする必要があります。)

注意: ワークベンチのメモリ要求を低下させるために絶対的に要求されない限り、プラグインとアクション・クラスはインスタンス化されません。マニフェスト・ファイルの内容は選択メニューを表示し、選択を使用許可/不可するために使われますが、HSQLDBサーバーはワークスペースリソースではないのでアクションを使用許可できません。リスト2のマニフェスト・コードに記述されているように(<enablement>要素を見てください)、プラグインがアクティブになっているかどうかによってアクションを使用許可/不可にできます。ですから起動時には単に「Start HSQLDB server」だけが使用許可されますが、その後は、例えば、既にサーバーが実行中であれば、このアクションを使用不可にする簡単な手段はありません。ですからサーバーが停止した時にアクションを再度使用許可します。

ワークベンチのウィンドウに砂時計マークを見せるためのコードとステータス行メッセージを設定するコードに注意してください。これらに関してはPDEのドキュメンテーションやEclipse.orgの記事の中でも簡単には見つかりません。


最終調整

このプラグイン・クラスはshutdownメソッドを上書きするので、ワークベンチが閉じた時にはきれいにサーバーを停止し、これで完成です。もちろん、完全なプラグイン・セットにするにはここでは説明しなかった追加作業が必要になります。例えば

  • インストール、削除、使用許可/不可が一組としてできるように、2つのプラグインをフィーチャーとしてパッケージする。
  • プラグイン自体のヘルプを用意し、ワークベンチのヘルプ・マネージャーに適した書式でHSQLDBのドキュメントを含める。

まとめ

この記事では、EclipseワークベンチにHSQLDBデータベースを追加するプラグインのセットを作成する方法を説明しました。また、データベース・サーバーを起動停止する機能の追加、SQLステートメントやスクリプトを実行する方法についても説明しました。さらに、PDEでこうしたプラグインの作成がいかに簡単になるかも見てきました。大部分の作業はウィザードやエディターを使ってできます。開発者がすることと言えば、必要な機能を実現するコードを書く作業にちょっと付け加える程度ですんでしまうのです。

このシリーズ次回の記事では(単に既存のツールを実行するのではなく)、ワークベンチのフィーチャーをいかに強化し、HSQLDB開発の価値を高めるかを学びます。

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, Java technology
ArticleID=236395
ArticleTitle=EclipseとHSQLDB: リレーショナル・データベース・サーバーをEclipseに埋め込む 第1回
publish-date=09302003