StringLoader
太一がEclipseプラグインとかでよくやっていたpropertiesファイルのメッセージを
public staticな変数にぶち込むやつをぱくってみた。名前もそのまんま。StringLoader。
使い方は、propertiesファイルの名前のpublic staticなフィールドにして並べておいて、
そのClassをStringLoaderにロードさせる。すると同名のpropertiesファイルを読んで、
キーと一致する変数名があれば設定してくれる。
テストはこんな感じ。
public class StringLoaderTest extends TestCase { public static String aaa; public static String bbb; public static String ccc; public void test1() throws Exception { StringLoader.load(StringLoaderTest.class); assertEquals("AAA", aaa); assertEquals("BBB", bbb); assertEquals("CCC", ccc); //propertiesの名前が違う場合 StringLoader.load(StringLoaderTest.class, "commons.util.StringLoaderTest2"); assertEquals("1", aaa); assertEquals("2", bbb); assertEquals("3", ccc); } }
StringLoaderTest.propertiesはこんな感じで。
aaa=AAA
bbb=BBB
ccc=CCC
実装。Java6前提でResourceBundleはキャッシュさせないようにした。
package commons.util; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.ResourceBundle.Control; import common.Logger; /** * StringLoader is an utility class to get messages from properties file. This * class is intended to use loading properties statically, and also is not * intend to be used multiple locale environments which message by locale is * dynamically switched by each user request. * * @author shot */ public class StringLoader { private static final Logger logger = Logger.getLogger(StringLoader.class); public static void load(Class<?> holder) { load(holder, holder.getName()); } public static void load(Class<?> holder, String name) { ResourceBundle rb = getBundle(name, holder.getClassLoader()); if (rb == null) { return; } Field[] fields = holder.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; if (validateMask(field)) { continue; } String key = field.getName(); if (rb.containsKey(key) == false) { logger.debug(key + " not found in " + name); } String msg = rb.getString(key); try { if (isAssignableFrom(String.class, field)) { field.set(null, msg); } } catch (Exception e) { logger.debug(e.getMessage() + e); } } } private static boolean validateMask(Field f) { final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC; final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL; return (f.getModifiers() & MOD_MASK) != MOD_EXPECTED; } private static ResourceBundle getBundle(String name, ClassLoader loader) { try { return ResourceBundle.getBundle(name, Locale.getDefault(), loader, new ResourceBundle.Control() { @Override public long getTimeToLive(String baseName, Locale locale) { return Control.TTL_DONT_CACHE; } }); } catch (MissingResourceException e) { return null; } } private static boolean isAssignableFrom(final Class<?> clazz, final Field target) { return clazz.isAssignableFrom(target.getType()); } }
課題は、JavaDocにも少し書いておいたけど、ロケールがころころ変わるような
環境ではこの方法じゃ駄目。なので、前提としてServerサイドのロケールだけを見て
一発でばーんと決まるようなメッセージ以外は使えないっすね。
これについては対処するコードも書いてみたんだけど、どうも気持ち悪いコードになるので却下。
なんとなくロケールがころころ変わるような場合においては、StringLoaderのアプローチは現実的じゃないので。