はじめに
- ConfigSlurperはGroovyスクリプト形式で書かれた設定ファイルを読み込むためのユーティリティクラスです。*1
- Groovy使い始め直後にConfigSlurperを使おうとしてハマった話です。
- ちなみにJavaではpropertiesファイルをよく使っています。
Propertiesのとき
- config.propertiesファイルを用意します。
hoge = ほげ
hoge.foo = ほげふー
class PropertiesSample {
public static void main(String... args) {
def properties = new Properties()
def file = new File("config.properties")
file.withReader("UTF-8", {
properties.load(it)
})
println properties.getProperty("hoge")
println properties.getProperty("hoge.foo")
}
}
ほげ
ほげふー
ConfigSlurperのとき
- config.groovyを用意します。
- Groovyスクリプトなのでpropertiesファイルとはちょっと変わります。
hoge = 'ほげ'
hoge.foo = 'ほげふー'
class ConfigSlurperSample {
public static void main(String... args) {
def config = new ConfigSlurper().parse(new File('config.groovy').text)
println config.hoge
println config.hoge.foo
}
}
- エラーになりました。MissingPropertyException。そんなプロパティはない??しかも、fooがないだと!?
Exception in thread "main" groovy.lang.MissingPropertyException: No such property: foo for class: java.lang.String
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:50)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.setProperty(ScriptBytecodeAdapter.java:482)
at script14811957302811365876559.run(script14811957302811365876559.groovy:2)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at org.codehaus.groovy.reflection.CachedMethod$invoke.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at groovy.util.ConfigSlurper$_parse_closure5.doCall(ConfigSlurper.groovy:266)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod.invoke(ClosureMetaMethod.java:80)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1108)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1016)
at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:149)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:39)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
at groovy.util.ConfigSlurper.parse(ConfigSlurper.groovy:284)
at groovy.util.ConfigSlurper$parse$0.callCurrent(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:145)
at groovy.util.ConfigSlurper.parse(ConfigSlurper.groovy:168)
at groovy.util.ConfigSlurper$parse.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at ConfigSlurperSample.main(ConfigSlurperSample.groovy:9)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
結論
- config.groovyのkeyの命名がイケてませんでした。
hoge
とhoge.foo
のような親と子両方に値は設定できません。
- config.groovyをクロージャで書いてみるとなんとなーく理由が想像つくような…
hoge {
foo = 'ほげふー'
}
hoge = 'ほげ'
- あちらを立てればこちらが立たず状態になってしまいますね…
終わりに
- Propertiesの感覚のままConfigSlurperのkeyを命名してしまうにはちょっと注意が必要です。
- ただそれを差し引いたとしてもConfigSlurperはめちゃくちゃ便利です。
- 明日はligunさんです。よろしくお願いします!