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