[Java How To Programming] [Home on 246net] [Home on Alles net]
Powered by SmartDoc

ResourceBundle.Controlで制御

TAKAHASHI, Toru
torutk@02.246.ne.jp

目次

ResourceBundle.Controlクラスをオーバーライドすることによって、リソースバンドルをより細かく制御できるようになります。

プログラム実行中にプロパティファイルの変更を反映

まず最初に、リソースバンドルのプロパティファイルを、プログラムの実行中に変更し、変更したプロパティ値を実行中のプログラムに反映させるプログラミングを行ってみます。

ソース全体を見る

ResourceBundleApp
package jp.gr.java_conf.torutk.coding.resourcebundle;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

/**
 * ResourceBundleのキャッシュ時間を制御し、プロパティファイルの更新を
 * プログラム実行中に反映する実験プログラム(クラス)。
 * 
 * @link ResourceBundle
 * @link ResourceBundle.Control
 */
public class ResourceBundleApp extends JFrame {
    private static Logger logger = Logger.getLogger(
        "jp.gr.java_conf.torutk.coding.resourcebundle"
    );
    private JButton updateButton;
    private JLabel messageLabel;
    private long timeToLive = 30 * 1000;
    private ResourceBundle.Control resourceBundleControl;

    /**
     * コンストラクタ。
     * リソースバンドル制御用のResourceBundle.Control匿名クラスを
     * 生成し、またGUIを構成するパーツを生成する。
     */
    public ResourceBundleApp() {
        super("ResourceBundle cache experiment");

        resourceBundleControl = new ResourceBundle.Control() {
                public long getTimeToLive(String aBaseName, Locale aLocale) {
                    return timeToLive;
                }
            };

        updateButton = createUpdateButton();
        add(updateButton, BorderLayout.SOUTH);
        messageLabel = createMessageLabel();
        add(messageLabel, BorderLayout.CENTER);
    }

    /**
     * 更新ボタンを生成する。
     * 更新ボタンのアクション
     */
    private JButton createUpdateButton() {
        JButton button = new JButton("Update");
        button.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent anEvent) {
                    String text = getMessageFromResource();
                    messageLabel.setText(text);
                }
            }
        );
        return button;
    }

    /**
     * メッセージ表示ラベルを生成する。
     */
    private JLabel createMessageLabel() {
        String text = getMessageFromResource();
        JLabel label = new JLabel(text);
        return label;
    }

    /**
     * リソースバンドルのキャッシュ時間を設定する。
     */
    public void setTimeToLive(final long aTimeToLive) {
        logger.info("setting new value = " + aTimeToLive);
        timeToLive = aTimeToLive;
    }

    /**
     * リソースバンドルからメッセージ文字列を取得する。
     *
     * リソースバンドル(プロパティ・ファイル)の更新を反映するには
     * 毎回getBundle(..)を呼び出す必要がある。
     * 
     * @throws MissingResourceBundle
     */
    private String getMessageFromResource() {
        long startTime = System.nanoTime();
        ResourceBundle resource = ResourceBundle.getBundle(
            "jp.gr.java_conf.torutk.coding.resourcebundle.MyResource",
            resourceBundleControl
        );
        long stopTime = System.nanoTime();

        String text = resource.getString("message");
        logger.info(
            "message = {" + text + "}, it takes " +
            (stopTime - startTime)/1000 + " us."
        );
        return text;
    }

    /**
     * リソースバンドル・キャッシュ実験プログラムを起動する。
     *
     * コマンドライン・オプション
     * <li>[-ttl <time to live>] キャッシュ時間(ms)<br>
     * ちなみに、-1を指定するとTTL_DONT_CACHE, -2を指定するとTTL_NO_EXPIRATION_CONTROLになります。
     * 
     */
    public static void main(final String[] args) {
        ResourceBundleApp app = new ResourceBundleApp();

        for (int i=0; i<args.length; ++i) {
            if ("-ttl".equals(args[i])) {
                app.setTimeToLive(Long.valueOf(args[++i]));
            }
        }
        
        app.setBounds(320, 100, 480, 256);
        app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        app.setVisible(true);
                
    }
}

リソースバンドルのキャッシュ制御

従来のリソースバンドル

Java 2 SE 5.0までのjava.util.ResourceBundleクラスは、一度プロパティファイルから取得した値はずっとメモリ中に保持し続けています。そのため、プログラムを実行中にプロパティファイルを変更しても、getBundleメソッドで取得したリソースバンドルは古い内容のままです。変更内容をプログラムに反映するにはプログラムの再起動が必要です。

J2SE 5.0までのリソースバンドル取得API
ResourceBundle resource = ResourceBundle.getBundle(
    "jp.gr.java_conf.torutk.coding.resourcebundle.MyResource"
);
String text = resource.getString("message");

Mustangで改善されたリソースバンドル

Java SE 6(Mustang)ではjava.util.ResourceBundleクラスが改善され、プロパティファイルの内容をメモリ中に保持している期間を制御することができるようになりました。

JavaSE6からのリソースバンドル取得API
ResourceBundle resource = ResourceBundle.getBundle(
    "jp.gr.java_conf.torutk.coding.resourcebundle.MyResource",
    new ResourceBundle.Control() {
        public long getTimeToLive(String baseName, Locale locale) {
	    return 60 * 1000;  // 60秒
	}
    }
);
String text = resource.getString("message");

getBundleメソッドの第2引数で、ResourceBundle.Controlクラスをオーバーライドしたクラスのインスタンスを指定します。キャッシュ制御に関してオーバーライドするのは、getTimeToLiveメソッドです。このメソッドの戻り値がキャッシュ有効期間(ミリ秒)となるので、適切な値を返却するようにオーバーライドします。また、キャッシュを無効にする、無期限キャッシュにするには次の定数を返却するようにします。

getTimeToLiveの返却で使用可能な定数
定数名 内容 定義値
ResourceBundle.Control.TTL_DONT_CACHE キャッシュ無効 -1
ResourceBundle.Control.TTL_NO_EXPIRATION_CONTROL キャッシュ有効期間を無限 -2