Apache commonsが便利な件(commons-configuration編)

久々のシリーズ。

今回はcommons-configuration。設定ファイル、ってありますよね。Javaだとproperties、Windosだとiniファイルが使われる事が多い。複雑なものだとXMLで書いたりする。

さて、そんなファイルの読み込み・書き出しってどうしますか。まさかFileInputStreamで自前で読み出すとか、しないですよね。コメント行の処理等、やらなきゃいけないことは結構あります。まぁ、propファイルだったらPropertiesクラスで読み書きできますが、それでも、そうそう便利には出来ていません。

XMLファイルだったりすると、DOM組んで読み書きしますかね。これも結構大仕事。

という時に使うのがcommons-configurationのようです。まぁ、能書きよりコードですかね。

propertiesファイルの場合

foo = hoge
foo.bar = 0
foo.baz = 1
bar = fuga
qux = 3.14
colors.header = #FF0000
name = ${foo} and ${bar}
PropertiesConfiguration config = new PropertiesConfiguration("sample.properties");
assertThat(config.getDouble("qux"), is(3.14));
assertThat(config.getString("colors.header"), is("#FF0000"));

ここまでは順当。なるほどですね。

Iterator<?> keys = config.getKeys();
while (keys.hasNext()) {
  String key = (String) keys.next();
  System.out.println(MessageFormat.format("{0} = {1}", key, config.getString(key)));
}

全読み出しの結果はこんなん。

foo = hoge
foo.bar = 0
foo.baz = 1
qux = 3.14
bar = fuga
colors.header = #FF0000
name = hoge and fuga

おお、${foo}とかも展開してくれるのですね。

Iterator<?> fooKeys = config.getKeys("foo");

アタマがfooのものだけを抽出、なんてのもこんな感じで。

config.addConfigurationListener(new ConfigurationListener() {
  
  public void configurationChanged(ConfigurationEvent event) {
    System.out.println(MessageFormat.format("{2}: {0} = {1}", new Object[] {
      event.getPropertyName(),
      event.getPropertyValue(),
      getTypeAsString(event)
    }));
  }
});

さらにリスナなんかを引っかけられたりして。

config.addProperty("quux", new Date());
config.setProperty("qux", 2.717);

とかやると、結果は以下のように。なぜ2回イベントが飛ぶのかはよくわからんw

EVENT_ADD_PROPERTY: quux = 09/10/22 20:58
EVENT_ADD_PROPERTY: quux = 09/10/22 20:58
EVENT_SET_PROPERTY: qux = 2.717
EVENT_SET_PROPERTY: qux = 2.717
StringWriter sw = new StringWriter();
config.save(sw);

Writerに書き出すのもコレだけです。明示的に書き出ししなくても、config.setAutoSave(true); とかすると、変更時には勝手にセーブしてくれるようです(未検証だけど)。

XMLの場合

<?xml version="1.0" encoding="UTF-8" ?>
<root>
  <colors>
    <background>#808080</background>
    <text>#000000</text>
    <header>#008000</header>
    <link normal="#000080" visited="#800080"/>
    <default>${colors.header}</default>
  </colors>
  <elements>
    <element type="1">a</element>
    <element type="1">b</element>
    <element type="2">c</element>
  </elements>
  <map>
    <entry>
  	  <key>a</key>
  	  <value>1</value>
    </entry>
    <entry>
  	  <key>b</key>
  	  <value>2</value>
    </entry>
  </map>
</root>
XMLConfiguration config = new XMLConfiguration("sample.xml");
assertThat(config.getString("colors.header"), is("#008000"));
assertThat(config.getString("elements.element(2)"), is("c"));
assertThat(config.getInt("elements.element(0)[@type]"), is(1));

XPathの廉価版みたいな感じでサクサクとアクセスできる。

List<String> elements = config.getList("elements.element");
for (String element : elements) {
  System.out.println(element);
}

複数要素もこんな感じで。

List<String> keys = config.getList("map.entry.key");
List<String> values = config.getList("map.entry.value");
System.out.println(keys.size());
for (String key : keys) {
  System.out.println(MessageFormat.format("{0}: {1} = {2}", new Object[] {
    keys.indexOf(key),
    key,
    values.get(keys.indexOf(key))
  }));
}

しかし、Map的なものはちょいと悩ましい感じだな。もっと良いAPIあるのかな? 未調査。


そんなヘンテコXPathモドキじゃなくて、XPathが使いたい!ってのならば。

config.setExpressionEngine(new XPathExpressionEngine());
assertThat(config.getString("elements/element[last()]/@type"), is("2"));

これでOK。但し、commons-jxpathもクラスパスに入れてあげてください。

config.addProperty("elements element", "d");
config.addProperty("elements/element[last()] @type", "2");

XPathにしてしまえば、要素の追加もこんなかんじ。出力してみると…。

(略)
<elements>
    <element type="1">a</element>
    <element type="1">b</element>
    <element type="2">c</element>
  <element type="2">d</element>
</elements>
(略)

あら、インデントが変だけど。まぁ何とか…。

その他

前述の、windows iniファイルも読めるらしい。JNDIのlookupも可能っぽい。システムプロパティもOK。

また、タイプの異なる複数の設定をマージしたりもできるのか〜。設定が分散していても、1個のオブジェクトでまとめて扱える感じ。

CompositeConfiguration config = new CompositeConfiguration();
config.addConfiguration(new SystemConfiguration());
config.addConfiguration(new PropertiesConfiguration("application.properties"));


…まぁ、使いドコロかなぁw