M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

クラスパスからプロパティ・ファイルを検索する

訳あって.class.jarを実行するときのクラスパスからプロパティ・ファイルを検索するロジックを考えることになった。・・・きちんとJavaについて理解している人であればそもそも考えるまでもないような話題のような気がするので恥ずかしい話なのだけれど。

いずれにせよ大したこともないコードになる。System . getProperty("java.class.path")でランタイムのクラスパス文字列を取得して、そこに含まれるディレクトリ配下や.jarの兄弟ファイルの中から.propertiesを探すだけである。

public final class Main {

	public static void main(String... args) {
		printWithLabel("java.class.path", System.getProperty("java.class.path"));
		printWithLabel("properties.file.list.from.class.path", getPropertiesFileListFromClassPath("foo.properties"));
	}
	
	private static List<File> getPropertiesFileListFromClassPath(final String propertiesFileName) {
		// システムのパス・セパレータを取得
		final String pathSeparator = System.getProperty("path.separator");
		// クラスパス文字列を取得
		final String javaClassPath = System.getProperty("java.class.path");
		// プロパティ・ファイルを格納するリスト
		final List<File> list = new ArrayList<File>();
		// 認識済みのファイルを記録するためのセット
		final Set<File> set = new HashSet<File>();
		// クラスパス文字列をパス・セパレータで分割してループ処理
		for (final String path : javaClassPath.split(pathSeparator)) {
			// クラスパスのエントリはディレクトリもしくはアーカイブ・ファイル
			final File directoryOrArchiveFile = new File(path);
			// もしファイルなら親ディレクトリを取得
			final File directory = directoryOrArchiveFile.isDirectory() ?
					directoryOrArchiveFile : directoryOrArchiveFile.getParentFile();
			// ディレクトリ配下に引数で指定されたプロパティ・ファイルがあると仮定
			final File propertiesFile = new File(directory, propertiesFileName);
			// もしファイルが実在し かつ 認識済みファイルでなければ
			if (propertiesFile.isFile() && set.add(propertiesFile)) {
				// ファイル・リストに追加
				list.add(propertiesFile);
			}
		}
		// 結果を返す
		return list;
	}

	private static void printWithLabel(String label, Object object) {
		System.out.println(String.format("%s => %s", label, object));
	}
}

アプリの設計次第で、このプロパティ・ファイルのリストを逆順にロードする(あるいはそもそもlist.add(0, propertiesFile)としておいて正順にロードする)とか、先頭もしくは末尾のもののみロードするとかが考えられる。

例えばApache Commons Configurationが提供するCombinedConfigurationを使って以下のようなコードを実装すれば、デフォルトの設定を必要に応じて上書きすることが可能なアプリケーションをつくることができる(CombinedConfiguration #addConfiguration(...)はすでに同名のプロパティが存在する場合、その項目を読み込まない。ということはつまり先にロードされるプロパティが後にロードされるそれを隠す≒上書きするということになる)。

final CombinedConfiguration combinedConfig = new CombinedConfiguration();
for (final File prop : getPropertiesFileListFromClassPath("foo.properties")) {
	combinedConfig.addConfiguration(new PropertiesConfiguration(prop));
}
combinedConfig.addnConfiguration(new PropertiesConfiguration(
	Main.class.getResource("foo.properties"));

リストには(これも設計次第だけど)カレントディレクトリ配下のプロパティファイルやクラスと同じディレクトリ=パッケージのプロパティファイルを追加してやってもいいかもしれない。