java

 

javaのpropertiesファイル汎用操作クラスを作成する!

javaのpropertiesクラスの汎用操作クラスを作成します。更にpropertiesのkeyをenum化する作業を自動化するクラスも作成します。おまけで、同一キーでサーバ毎に値だけを変えたい要望に答えられる機能も実装しています。

新サイト、tree-mapsを公開しました!!

tree-maps: 地図のWEB TOOLの事ならtree-mapsにお任せ!

地図に関するWEB TOOL専門サイトです!!

大画面で大量の緯度経度を一気にプロット、ジオコーディング、DMS<->DEGの相互変換等ができます!

◯ 広告

javaのpropertiesは、java標準の設定ファイルの機能です。

標準機能のみでコーディングすると以下のようになります。

try {
    Properties prop = new Properties();
    InputStream inputStream = new FileInputStream(new File(
            "/project/hoge-project/src/main/resources/config/config.properties"));
    prop.load(inputStream);
    String s = prop.getProperty("domain");
    System.out.println(s);
} catch (IOException e) {
    e.printStackTrace();
}

このままで使いにくいし、"domain"の部分がハードコーディングになってしまっています。

ここでは問題点を以下の2点と定め、それぞれ解決していきます。

  • プロパティファイルから値を取得するクラスが使いにくい。
  • プロパティのキーが定数化されていない。

システムプロパティのキーを定数化(enum化)したクラスです。

このkeyクラスは後述で自動生成する対象のクラスです

public enum ConfigKey {
    domain,
    ;
}

enum化しているのでeclipse等のリファクタ機能でリネームも簡単です。

プロパティファイルの構成は以下の通りです。

hoge-project/
└── src
    └── main
        └── resources
            └── config
                ├── config-server.properties
                └── config.properties

今回はseasarのutilを使い、汎用的な操作クラスを作成します。

値を取得する際に、文字列・数値・リスト等を選択できるようにします。

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static org.seasar.framework.util.ResourceUtil.getProperties;
import static org.seasar.framework.util.ResourceUtil.isExist;

import java.util.List;
import java.util.Properties;

import com.google.common.base.Strings;

public class Config {

    private static String propPath = "config/config.properties";
    private static Properties prop = isExist(propPath) ? getProperties(propPath) : null;
    private static String serverPropPath = "config/config-server.properties";
    private static Properties serverProp = isExist(serverPropPath) ? getProperties(serverPropPath) : null;
    private static List<Properties> props = newArrayList(prop, serverProp);
    private static final String SPLITTER = ",";

    public static String getString(ConfigKey key) {
        String value = null;
        for (Properties p : props) {
            if (p == null)
                continue;
            String s = p.getProperty(key.toString());
            if (Strings.isNullOrEmpty(s))
                continue;
            value = s;
        }
        return value;
    }

    public static List<String> getStrings(ConfigKey key) {
        return newArrayList(getString(key).split(SPLITTER));
    }

    public static Integer getInt(ConfigKey key) {
        return Integer.valueOf(getString(key));
    }

    public static List<Integer> getInts(ConfigKey key) {
        String[] array = getString(key).split(SPLITTER);
        List<Integer> list = newArrayListWithCapacity(array.length);
        for (String s : array)
            list.add(Integer.valueOf(s));
        return list;
    }

    public static Double getDouble(ConfigKey key) {
        return Double.valueOf(getString(key));
    }

    public static List<Double> getDoubles(ConfigKey key) {
        String[] array = getString(key).split(SPLITTER);
        List<Double> list = newArrayListWithCapacity(array.length);
        for (String s : array)
            list.add(Double.valueOf(s));
        return list;
    }
}

ConfigKeyというのがenum化したクラスです。後述で説明します。

このクラスには1つ特殊な機能を仕込んでいます。

それは、「サーバ毎にプロパティファイルの値を変える機能」です。

ローカルの場合は「c:¥log¥hoge」だが、サーバでは「/var/log/hoge」にしたい等の要望に答えられます。

config.propertiesは、基準となるプロパティファイルです。

config-server.propertiesは、config.propertiesの値を上書きするプロパティファイルです。

config-server.propertiesには上書きしたいキーのみを記述します

実践では、ステージング環境等のビルドをする際に、config-server.propertiesをコピーします。

よくあるのが、以下のように環境毎にフォルダを作成し、それぞれ別の値を記述したファイルを用意する事があります。

environment/
├── develop
│   └── config
│       └── config-server.properties
├── product
│   └── config
│       └── config-server.properties
└── staging
    └── config
        └── config-server.properties

ビルド時に環境にあったconfig-server.propertiesをconfigにコピーすればOKです。

まずはプロパティファイルです。

list=1,2,3

半角カンマ区切りで数値を設定しています。

続いて実行するためのmainメソッドです。

public static void main(String[] args) {
    List<Integer> list = Config.getInts(ConfigKey.list);
    System.out.println(list);
}

↓↓↓
[1, 2, 3]

このくらい簡単に扱えるようになります。

これを実行するとConfigKeyが自動生成されます

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Charsets;
import com.google.common.io.Files;

public class ConfigKeyGeneratory {

    private static Logger LOGGER = LoggerFactory.getLogger(ConfigKeyGeneratory.class);
    private static final String L = "\n";
    private static final String T = "\t";
    private static final String PACHAGE = "tree.s2.config;";
    private static final String BASE_DIR = "/project/hoge-project/base/src/main";
    private static final String RESOURCE_PATH = BASE_DIR + "/resources/config/config.properties";
    private static final String JAVA_PATH = BASE_DIR + "/java/tree/s2/config/ConfigKey.java";

    public static void main(String[] args) {
        LOGGER.info("ConfigKeyの生成開始。");
        generate();
        LOGGER.info("ConfigKeyの生成終了。");
    }

    private static void generate() {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            br = Files.newReader(new File(RESOURCE_PATH), Charsets.UTF_8);
            bw = Files.newWriter(new File(JAVA_PATH), Charsets.UTF_8);

            bw.write("package " + PACHAGE + L);
            bw.write(L);
            bw.write(L);
            bw.write("public enum ConfigKey {" + L);

            String line = "";
            while ((line = br.readLine()) != null) {
                if (line.startsWith("#"))
                    continue;
                String[] keyValue = line.split("=");
                String key = keyValue[0];
                bw.write(T + key + "," + L);
            }
            bw.write(T + ";" + L);
            bw.write("}");
            bw.flush();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(br);
            IOUtils.closeQuietly(bw);
        }
    }
}

propertiesをパースし、コメント行以外を抽出してenumのjavaファイルを生成しているだけです。

これでKeyクラスを自動生成できるので、以下のような完全自動化も可能です。

  • 開発者がpropertiesファイルをコミット。
  • サーバ側でKeyを自動生成するcronを設定し、自動生成したクラスが今回と前回で差分があるか比較。
  • 差分があった場合、scmにKeyクラスをコミットする。
  • 開発者は自動コミットされたKeyクラスを取得して利用する。
◯ 広告