Java

Preferences API の概要

はじめに

設定データを変更するすべてのメソッドは、非同期的に操作できます。各メソッドはただちに復帰し、変更は持続的なバッキングストアに転送されます。flush メソッドを使用すれば、バッキングストアを強制的に更新できます。

Preferences クラス内のメソッドは、単一 JVM 内の複数スレッドによって同時に呼び出されます。このとき、外部同期を行う必要はありません。逐次実行した場合と同じ結果になります。このクラスが複数の JVM によって同時に使用され、設定データが同じバッキングストアに格納された場合、データストアは破壊しませんが、設定データの一貫性は保証されません。

詳細については、以下のリンクから選択してください。


Preferences API とほかの機構との比較

Preferences API を導入する以前は、設定および構成データを個別に管理するために、これから説明する Properties API または JNDI API を使用していました。

設定および構成データは、多くの場合、java.util.Properties API を使用してプロパティーファイルにアクセスし、このファイルに格納していました。ただし、ディスク上でのファイルの位置またはファイルの名前に関する標準は存在しませんでした。この場合、ユーザーの設定データのバックアップ作成や、マシン間のデータ転送が困難になります。アプリケーション数が増加するにつれて、ファイル名が重複する可能性が高くなります。また、ローカルディスクが存在しない場合、またはデータを外部データストア (企業全域にわたる LDAP ディレクトリサービスなどの) に保存する場合には、この方法は使用できません。

適用例は少なくなりますが、JNDI (Java Naming and Directory Interface) API を使用してディレクトリサービスにアクセスし、そこに格納する場合もありました。Properties API と異なり、JNDI では、任意のデータストアを使用できます (バックエンドの中立性)。JNDI は強力な API ですが、サイズが比較的大きく、5 つのパッケージと 83 のクラスから構成されます。JNDI には、ディレクトリ名前空間内で設定データを格納する場所、または格納する名前空間に関するポリシーが存在しません。

Properties および JNDI には、単純で、汎用的な、バックエンドの中立性を持つ設定管理機能はありません。Preferences API では、Properties API の単純さと JNDI のバックエンドの中立性が同時に実現されます。Preferences API には、名前の重複を回避し、一貫性を保持し、安定性を向上するために必要なポリシーが組み込まれています。ただし、バッキングデータストアの使用は推奨されていません。


使用上の注意

ここでは、Preferences API の仕様を説明するのではなく、Preferences API の使用例をいくつか示します。

包含クラスの Preferences オブジェクトを取得する

次の例では、包含クラスに所属する Preferences オブジェクト (システムおよびユーザー) を取得する方法を示します。これらの例は、インスタンスメソッド内にのみ動作します。

ここでは、インラインの String リテラルではなく、static final フィールドが、キー名 (NUM_ROWS および NUM_COLS) として使用されています。このようにすると、キー名の入力ミスによる実行時のバグが発生する可能性が減少します。

取得した各設定値には、適切なデフォルトが割り当てられます。これらのデフォルトは、設定値が設定されていない場合、またはバッキングストアにアクセスできない場合に返されます。

package com.acme.widget;
import  java.util.prefs.*;

public class Gadget {
    // Preference keys for this package
    private static final String NUM_ROWS = "num_rows";
    private static final String NUM_COLS = "num_cols";

    void foo() {
        Preferences prefs = Preferences.userNodeForPackage(Gadget.class);

        int numRows = prefs.getInt(NUM_ROWS, 40);
        int numCols = prefs.getInt(NUM_COLS, 80);

        ...
    }
}

上の例では、ユーザーごとの設定値を取得しています。システムごとの値だけが必要な場合は、foo の最初の行を次の行に置き換えます。

        Preferences prefs = Preferences.systemNodeForPackage(Gadget.class);

Static メソッドの Preferences オブジェクトを取得する

前の節では、包含クラスに所属する Preferences オブジェクトを取得し、インスタンスメソッドの内部を操作しました。Static メソッド (または静的初期化子) 内では、次のように、パッケージ名を明示的に指定する必要があります。

    Static String ourNodeName = "/com/acme/widget";

    static void foo() {
        Preferences prefs = Preferences.userRoot().node(ourNodeName);

        ...
    }

通常は、システム設定オブジェクトを初期化子内で 1 回取得し、システム設定が必要なときにそれを使用します。

    static Preferences prefs =  Preferences.systemRoot().node(ourNodeName);

通常は、ユーザー設定オブジェクトの場合も、同じ方法を適用します。ただし、コードをサーバー内で使用し、複数のユーザーが同時にまたは順番に実行する場合は、この方法は使用しません。このようなシステムでは、userNodeForPackage および userRoot が呼び出し側のクライアントに対して適切なノードを返します。つまり、userNodeForPackage または userRoot への呼び出しは、適切なスレッドから適切なタイミングに行うことが重要になってきます。このようなサーバー環境でコードを使用する場合は、使用する直前にユーザー設定オブジェクトを取得することをお勧めします。「包含クラスの Preferences オブジェクトを取得する」を参照してください。

不可分な更新

Preferences API には、複数の設定が不可分に変更されるトランザクションに似たデータベースはありません。ただし、複数の設定を更新するときは、1 単位で行う必要があります。たとえば、x 座標と y 座標を格納して、そこにウィンドウを配置することを想定します。不可分に更新するには、これらの値を単一の設定に格納します。さまざまな方法でコーディングできますが、ここでは簡単な例を示します。

    int x, y;
    ...
    prefs.put(POSITION, x + "," + y);

このような複合設定を読み込むときは、復号化する必要があります。安定性を確保するために、値が破壊した (解析不可能な) 場合を考慮する必要があります。

    static int X_DEFAULT = 50, Y_DEFAULT = 25;
    void baz() {
        String position = prefs.get(POSITION, X_DEFAULT + "," + Y_DEFAULT);
        int x, y;
        try {
            int i = position.indexOf(',');
            x = Integer.parseInt(coordinates.substring(0, i));
            y = Integer.parseInt(position.substring(i + 1));
        } catch(Exception e) {
            // Value was corrupt, just use defaults
            x = X_DEFAULT;
            y = Y_DEFAULT;
        }
        ...
    }

バッキングストアのステータスを判断する

標準のアプリケーションコードでは、バッキングストアを利用できるかどうかに関する情報は必要ありません。ほとんどの場合、バッキングストアは常に利用できます。利用できない場合は、バッキングストア内の設定値の代わりにデフォルト値を使用して、実行を継続します。一部の高度なプログラムでは、バッキングストアを利用できない場合に、動作を変更する (または単純に実行を拒否する) ことができます。以下のメソッドでは、バッキングストアを利用できるかどうかを判断するために、値を変更した設定をバッキングストアにフラッシュしています。

    private static final String BACKING_STORE_AVAIL = "BackingStoreAvail";

    private static boolean backingStoreAvailable() {
        Preferences prefs = Preferences.userRoot().node("<temporary>");
        try {
            boolean oldValue = prefs.getBoolean(BACKING_STORE_AVAIL, false);
            prefs.putBoolean(BACKING_STORE_AVAIL, !oldValue);
            prefs.flush();
        } catch(BackingStoreException e) {
            return false;
        }
        return true;
    }

Copyright © 2006 Sun Microsystems, Inc. All Rights Reserved.

コメントの送付先: TBP
Sun
Java Software