2000-2-22 Pennington Havoc
hp@redhat.com
2000 Havoc Pennington GConf ライブラリ概論 GConf 概論 この記事では、GNOME 開発環境の次期メジャーリビジョンから含まれ る予定である、GConf 設定ライブラリの背景にある概念を紹介します。
概論: GConf とは何か? GConf は GNOME 2.0 から含まれる予定である、設定データの保管機構 です。GConf は GNOME 無しでも動作します。つまりこれは素の GTK+ や Xlib, KDEあるいはテキストモードのアプリケーションでも使えます。 2000 年 3 月の時点では、GConf の安定版のリリースはまだですが、ラ イブラリは完成しています。 GNOME デスクトップは現在のところアプリケーションの設定に関しては Windows 3.1 の段階です。つまり、アプリケーションはその設定をフラッ トな .INI 形式のファイルに保存します。Windows は後にレジストリと いうより洗練された解決策を導入しています。しかしながら、レジスト リでもまだ多数の欠点を抱えています: 膨大な計算機管理が困難。これは、システム管理者が複数の ユーザの計算機に既定値をインストールしたり、変更を加えたり できない点です。様々な(幾分ぞっとするような)方法でこの問題 を解決する専用のアドオンツールは存在します。 レジストリには文書化されていない不可解な形式のデータを多 数含んでいるために、regedit を使うのは危険で難しいのです。 レジストリは汚れつつあり、オペレーティングシステムのインス トール状態そのものを破壊する傾向にあります。 GConf はレジストリの概念を飛び越えようとしています。これはアプリ ケーションに簡潔な設定データ保存インタフェースを提供する ライブラリであり、システム管理者が容易に何 か行えるようなアーキティクチャでもあります。 これが GConf の代表的な特徴です: 置換可能なバックエンドアーキティクチャ。GConf は現在のとこ ろ設定データを XML 形式のテキストファイルに保存するバック エンドを一つ持っています。しかしながら、このアーキティクチャ はレジストリのようなバイナリデータベースのバックエンド、 LDAP バックエンド、あるいは成熟した SQL データベースバック エンドも可能です。 使用されるバックエンドはシステム管理者によって設定可能です。 これは情報科学部門で膨大な計算機を管理するには効果的です。 設定キーの文書。アプリケーションは設定データに加えて、許さ れる設定や各設定キーの効果を説明した文書をインストールでき ます。regedit 形式のツールは文書表示が可能なため、アプリケー ションを壊すことなく容易にカスタマイズすることができます。 GConf データベースはまた、最後にキーが変更された時にキーを 保持しているアプリケーションといった便利なメタ情報も保持で きます。 データ変更通知サービス。設定データが変更された際、関連する アプリケーションは通知を受けます。通知サービスはネットワー クを経由して機能し、利用者個人の全ログインセッションに反映 されます。 コンポーネント(各コンポーネントは別々のプロセスかもしれま せん)からのプログラム構築はより簡単に記述できます。なぜな ら、あるコンポーネントが設定を変更した場合、他方は変更を発 見でき自身を更新できるからです。例えば、GNOME の新しい Nautilus ファイルマネージャは現にアプリケーションの集合で あり、埋め込みウェブブラウザや、様々なナビゲーションコンポー ネントを含んでいます。コンポーネントは CORBA を経由して情 報伝達します。ところがあなたは、単一のプリファレンスダイア ログは、その最上位の「シェル」コンポーネントに配置されてい て欲しいでしょう。GConf の無い場合、埋め込まれたコンポーネ ントの変更を利用者のプリファレンスに通知するために、各プリ ファレンス設定ごとに専用のプロトコルが作成されるはめになり ます。 複数のアプリケーションの実体が実行されている場合、「通知」 も便利です。GNOME 2.0 はどんなアプリケーションであれ、再起 動せずにその場でユーザインタフェースの設定を有効にさせるた めにこの機能を用います。つまり、例えばもしあなたがツールバー アイコンを外したら、実行中の全てのアプリケーションのツール バーアイコンが直ちに姿を消すでしょう。 クライアント API は非常に抽象的で、後でさほど悩まずに実装 変更できるようになっています。なぜなら良い実装とは難しい問 題だからで、これは重要です。私も API は簡単に使えるように 考えたいものです。 GConf は、複数のアプリケーションが設定データを使う時にこれ を汚染しないように正しくロックを行います。 実装の概観 キーと値 GConf は成熟したデータベースを目指すつもりはありません。単にキー と値の組を保存するだけです。これは実装を簡潔で効率的なものに維 持します。特に、次のデータベースの性質は現在実装されていません: クエリー 巨大な塊のデータの効率的な保存 (ドキュメントまるごとなど) トランザクション これらの高度な機能が必要なアプリケーションには LDAP や完全な規 模のデータベースを使うべきです。トランザクションはゆくゆくは追 加されるでしょう。これは現在 GConfChangeSet オブジェクトの形式で API にありますが、今のところチェンジセットはアトミックには保存 されません。 GConf キーは UNIX のファイル名に似ています。キーは UNIX のファ イルのが置かれるように、「ディレクトリ」に整理されています。例 えば、/desktop ディレクトリはデスクトップの 設定は保存し、仮想キー /desktop/standard/background-color デスクトップのバックグラウンドの色を保存するかもしれません。 GConf 文書は幾つかのお約束のディレクトリを定めています。自分の アプリケーションキーを命名する時はこの文書のガイドラインに従う べきです。 値は型を持ちます。プリミティブな型は整数、論理値、文字列、浮動 小数点数です。また、プリミティブな型のリストやペアも可能です。 再帰的な型(リストのリスト)は許されません。型は大きさの制限を受 けません。つまり、文字列は好きなだけ長くできますから、無闇に長 い文字列(XML文書全体など)はパフォーマンスの問題を起こしてしま うでしょう。 設定サーバ GConf はgconfdとよばれる個人毎のデー モンとして実装されています。gconfd は適切なバックエンドモジュールを動的に読み込んで、実際に利用者 の設定バックエンドにアクセスします。 gconfd はまた、設定値が修正された時 に関連するアプリケーションに通知を送り出す役割を担っています。 gconfd は常に利用者一人あたり一つ存 在しているはずです。利用者が同時に二つの計算機にログインした場 合、gconfd は最初の計算機で実行され、 二番目の計算機はネットワーク越しにそれをアクセスします。 gconfd は CORBA を通信路として用い てアプリケーションと対話します。これは実装依存の細かな事です。 つまり、ソケット上の専用プロトコルが使われていたり、将来使われ ることもあり得ます。CORBA インタフェースへの直接アクセスは サポートされません。アプリケーションプロ グラマは GConf を使っていれば CORBA に遭遇することはありません。 一般に、gconfd はアプリケーションプ ログラマには不可視です。これは必要な時に自動的に起動されます。 (内部的には、gconfd CORBA サーバの オブジェクト参照を得るために OAF オブジェクト活性化フレームワー クが使用されます)。 全てのデータベースアクセスは gconfd 経由で行われるため、ロックは全く議論されません。 gconfd はまた積極的に設定値をキャッ シュするため、全てのアプリケーションが同じキャッシュを共有でき ます。 スキーマ 先に述べたプリミティブ型(整数、文字列など)に加えて、GConf デー タベースはスキーマを保存できます。スキー マはキーの説明です。すなわち、キーに関する簡単な説明、長い文書 の段落、キーの型、キーのデフォルト値、キーを保持するアプリケー ション名を含みます。スキーマは regedit 形式のツールで使うこと を意図しています。この種のツールはキーの解説を表示し、キーに対 して利用者が入力した値の認証を行います。 スキーマはデータベース中では単なる値ですので、キー名で参照する ことができます。便宜上、全てのスキーマは トップディレクトリ /schemas の下に置かれま す。 アプリケーションは、スキーマ情報を人間が読めるテキスト形式にエ ンコードした特殊な スキーマファイル に 関連付けられます。アプリケーションがインストールされると、 スキーマファイルをシステムのディレクトリ(GConf ドキュメントに 指定されています)にインストールするために、GConf で用意されて いる gconftool プログラムが使われま す。これは、システム管理者が、全スキーマ情報を異なる GConf バッ クエンドに、コマンド一つ(gconftool --install-schema-file /etc/gconf/schemas/*など)で再 インストールできるようにするものです。 アプリケーションプログラミング GConf はアプリケーションの設定ファイルを保存したり取得したりをで きるだけ簡単に行います。しかしながら、一般の設定データの保存や取 り出しの構造はあなたが設定ファイルで使うものとは幾分異なります。 Model-View-Controller アーキティクチャ GConf を効率的に用いたアプリケーションは、有名な Model-View-Controller (MVC) デザインパ ターンに従ってこれらのプリファレンスや設定コードを構成します。 このパターンでは、アプリケーションには3つの分かれたオブジェク トが含まれます: モデルは実世界の対象をモデル化し ます。しかしながら多くの場合は、利用者に提供して編集させ たいと考えるデータです。GConf の場合だと、モデルは(大ま かには)設定データベースであり(小さな観点からは)個々の設 定キーです。例えば、 /desktop/gnome/menus/show-icons はモデルと考 えることができます。これは次回から GNOME メニュー中のメ ニュー項目に、アイコンを表示するかどうか指示する論理値を 保存します。 ビューはモデルの表示あるいは表現 方法です。これはモデルが変更された時に何らかの通知を受け、 それに応じて自身を更新します。例えば、GNOME メニューアイ テムウィジェットは /desktop/gnome/menus/show-icons 設定キーの 変更に注意を向けていて、それに応じてアイコンを表示したり 隠したりします。ビューの重要な点は一つ以上存在できること です。つまりモデルを変更せずに多くのメニューアイテムを持 つことができます。 コントローラ はモデルを修正する。 メニューアイコンの例では、GNOME コントロールパネルがコン トローラとなる。利用者はこれを利用して /desktop/gnome/menus/show-icons キーを true か false に設定する。 MVC アーキテクチャには多くの利点があります。特にこれは、コード の再利用とモジュール性を促進します。モデルやコントローラを変更 せずに、幾つのビューでもどんな種類のビューでも持てます。主なコ ントローラは GNOME コントロールパネルとなりますが、他のアプリ ケーションからの変更でも、おそらく同様に機能するでしょう。主な ビューは GNOME メニューアイテムのプログラムとなりますが、この 設定を監視し守るために、異なるメニューアイテムのプログラムが望 まれる場合は、もちろんできます。 GConf は基本的な MVC パターンへの興味をそそる機能強化を実装し ています。すなわち、モデルがプロセス透過なのです。これは、ビュー とコントローラはどのプロセスがモデルを保持しているかという知識 を必要としません。もし設定キーを設定すると、関心あるプロセス中 の全てのビューは変更を通知されます。コンポーネント技術の世界で は、これは非常に有用です。 主なデータ型 <structname>GConfEngine</structname> GConfEngineは設定データベース (通常 はgconfdへの接続) を提供します。 値はGConfEngineに保存でき、再びそこ から値を取り出せます。また、キーやディレクトリの変更を監視す るためにGConfEngineの質問を行え、 変更が起きた時にはアプリケーションが提供しているコールバック を呼び出せます。変更を通知するために 毎回GConfEngineを問合せますが、これ はgconfdにより通知のリクエストを 登録します。すなわち、通知のリクエストは比較的高くつきます。 GConfClientと同じで、これれは比較的 安っぽいです。 changes, it registers a request for notification with gconfd. Thus, notification requests are relatively expensive. With GConfClient, they are relatively cheap. <structname>GConfClient</structname> GConfClientは GConf 自身とは分離さ れたライブラリに位置付けられます。なぜなら、これを使うため には GTK+ とリンクする必要があるからです。 GConfClientGtkObjectから派生していて GConfEngine型のラッパーです。これは 幾つかの気の利いた特徴を加えています: これは値のクライアント側のキャッシュを保持します。しば しばこれは単なるメモリの無駄遣いになりますので、オフに したいことでしょう。しかしながら、同じキーを頻繁にアク セスする場合はパフォーマンスの方が勝ります。 これはクライアント側の通知リクエストの一括処理が行えま す。キー/foo/barおよび /foo/bazを監視したい場合、 GConfClientgconfdを使って単一の通知リ クエストを登録します。通知が gconfdから来た場合、 GConfClientはどのキーが変更さ れ個々のアプリケーションコールバックが一括処理されるか を決定します。GConfEngineの場 合、各コールバックはgconfd により登録されているリクエストを必要とします。 これは GTK シグナルシステムを使用します。代わりに通知 にGConfのカスタムコールバック API を使うと、 "value_changed" シグナルを接続する だけです。 最後に、GConfClientはデフォル トのエラーハンドラを提供します。これはエラーを無視させ ることが可能で、GConfClient はこれらのエラーをエラーダイアログを通してユーザに提供 できます。 <structname>GConfValue</structname> GConf が、動的な型付き値をデータベースから取り出したり保存し たりするために渡す必要がある場合、 GConfValueとよばれる型付きの共用体 を使います。例えば、gconf_get()関数は GConfEngineやキー名を引数として取り、 GConfValueを返します。 gconf_get_string()gconf_get_int() といった便利なラッパー がありますので、やっかいな GConfValue共用体を避けることができ ます。 <structname>GConfChangeSet</structname> GConf データベースへの修正はチェンジセット 、すなわちチェンジ(変更)の集まりの中でまとめるこ とができます。チェンジとはキーに対する新たな値の割付や、キー の現在の値のを外すことです。 GConfChangeSetは第一に、利用者がプ リファレンスダイアログ経由で作ったチェンジの集まりを管理する ための便利なメカニズムです。しかしながら、将来的にはチェンジ セットの委譲はアトミックな操作になるでしょう(つまり、GConfは トランザクションをサポートするように拡張されるのです)。 <structname>GConfError</structname> もし明示的にエラーを操作したい場合は、利用者にエラー報告する GConfClientのデフォルトハンドラを使 うのではなく、GConfErrorと呼ばれる オブジェクト中のエラーに関する情報を受け取ります。これには多 量のエラー詳細とエラーコード番号が含まれています。 GNOME 統合 GNOME のバージョン 2.0 はより簡単にライブラリを扱えるよう、自 動的に GConf を統合しています。これは次のことを行います: グローバルなGConfClientを保持し ていて、gnome_get_gconf_client()関 数から容易にアクセスできます。 デフォルトエラーハンドリングとして、自動的に GNOME ダイ アログを用います。 様々な種類のウィジェットから GConfValueを取り出したり挿入す るための便利な関数を提供します。例えば、 GtkEntryの内部の文字列を GConfValueとして取得する関数が あります。 プログラム例 この節では二つの簡単なプログラム例を示します。一つ目はキーの値を 表示し(そしてキーが変更された時には表示される値が更新されます)、 もう一つは同じキーの値の変更を行います。これらの例は MVC アーキ テクチャをより具体的なものとし、GConf API の一般的な動作概念を理 解できます。 この例ではGConfClientを使用しますが、 GTK とのリンクを避けるためにGConfEngine を直接使えることを忘れないようにしてください。また、次期バージョ ンの GTK と glib ではオブジェクトシステムは glib に移行しますの で、GConfClientはメイン GConf ライブラ リに移行します。 簡単なビュー このプログラムは、キー /extra/test/directory/keyの現在の値を表示す るGtkLabelを作成します。(ついでですが、 ディレクトリは GConf 文書で説明されているように約束された名前 を持っています。この場合だと、ディレクトリ /extraは、メールのヘッダやMIME型での X-プリフィックスに似ていて、非標準な拡張を 標準化するためのものです。ここでの非標準な拡張は、取るに足りな い小さなテストプログラムです。) この記事は単なる GConf の概観を説明するためだけで、完全なチュー トリアルではありませんので、このプログラムの全詳細を説明するこ とはできません。ここで出逢う関数やデータ型の詳細は GConf 文書 にあります。 /* たった一つのキー変更を監視する非常に簡単なプログラム。*/ #include <gconf/gconf-client.h> #include <gtk/gtk.h> void key_changed_callback(GConfClient* client, guint cnxn_id, const gchar* key, GConfValue* value, gboolean is_default, gpointer user_data) { GtkWidget* label; label = GTK_WIDGET(user_data); if (value == NULL) { gtk_label_set(GTK_LABEL(label), "<unset>"); } else { if (value->type == GCONF_VALUE_STRING) { gtk_label_set(GTK_LABEL(label), gconf_value_string(value)); } else { gtk_label_set(GTK_LABEL(label), "<wrong type>"); } } } int main(int argc, char** argv) { GtkWidget* window; GtkWidget* label; GConfClient* client; gchar* str; gtk_init(&argc, &argv); gconf_init(argc, argv, NULL); client = gconf_client_new(); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); str = gconf_client_get_string(client, "/extra/test/directory/key", NULL); label = gtk_label_new(str ? str : "<unset>"); if (str) g_free(str); gtk_container_add(GTK_CONTAINER(window), label); gconf_client_add_dir(client, "/extra/test/directory", GCONF_CLIENT_PRELOAD_NONE, NULL); gconf_client_notify_add(client, "/extra/test/directory/key", key_changed_callback, label, NULL, NULL); gtk_widget_show_all(window); gtk_main(); return 0; } 簡単なコントローラ このコントローラはエントリーボックスを持つウィンドウをポップアッ プします。エントリーボックス内でキーボードを打ってリターンを押 した場合、ボックスの内容はキー /extra/test/directory/keyの新たな値となりま す。ビュープログラムを走らせている場合は、新たな値を反映してラ ベルが更新されるでしょう。 繰り返しますが、完全な詳細は GConf 文書を参照下さい。 /* エントリー中でキーボードを打ってリターンを押した時に、 キーの値を一つだけ設定する非常に簡単なプログラム */ #include <gconf/gconf-client.h> #include <gtk/gtk.h> void entry_activated_callback(GtkWidget* entry, gpointer user_data) { GConfClient* client; gchar* str; client = GCONF_CLIENT(user_data); str = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1); gconf_client_set_string(client, "/extra/test/directory/key", str, NULL); g_free(str); } int main(int argc, char** argv) { GtkWidget* window; GtkWidget* entry; GConfClient* client; gtk_init(&argc, &argv); gconf_init(argc, argv, NULL); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); entry = gtk_entry_new(); gtk_container_add(GTK_CONTAINER(window), entry); client = gconf_client_new(); gconf_client_add_dir(client, "/extra/test/directory", GCONF_CLIENT_PRELOAD_NONE, NULL); gtk_signal_connect(GTK_OBJECT(entry), "activate", GTK_SIGNAL_FUNC(entry_activated_callback), client); gtk_widget_show_all(window); gtk_main(); return 0; } その他の情報源 GNOME 開発者サイト GConf 文書