ksino's diary

覚えたことを忘れないように、小さなことでも書いていく。

JAR内の特定のディレクトリに格納された定義ファイルをすべて読み込む

やりたいこと

JAR内の特定のディレクトリに格納された定義ファイルをすべて読み込みたい。
例えばJARの構成が以下であった場合、messagesディレクトリに格納された*.propertiesを読み込みます。

xxx.jar
├META-INF/
| └MANIFEST.MF
├com/
| └example/
|  └Test.class
└messages/
 ├a.properties
 └b.properties

実装

クラスパス上のリソースファイルを読み込むためには一般的にgetResourceAsStreamを使いますが、JAR内の場合はひと工夫必要らしいです。

package jp.gr.java_conf.ksino.readmessages;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 *
 * @author ksino
 */
public class Main {

    private static final String MESSAGES_DIR = "messages/";

    public static void main(String[] args) throws IOException {
        loadMessages();
    }

    private static void loadMessages() {
        ClassLoader classLoader = Main.class.getClassLoader();
        URL url = classLoader.getResource(MESSAGES_DIR);
        if (url == null) {
            // 例外処理
        } else {
            if (url.getProtocol().equals("jar")) {
                // 定義ファイルがJAR内の場合
                String[] s = url.getPath().split(":");
                String path = s[s.length - 1].split("!")[0];
                File f = new File(path);
                JarFile jarFile;
                try {
                    jarFile = new JarFile(f);
                    Enumeration<JarEntry> entries = jarFile.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry entry = entries.nextElement();
                        String name = entry.getName();
                        if (name != null && name.startsWith(MESSAGES_DIR)) {
                            try (InputStream is = classLoader.getResourceAsStream(name);) {
                                read(is);
                            }
                        }
                    }
                } catch (IOException ex) {
                    //例外処理
                }
            } else {
                // 定義ファイルがJAR外の場合
                File dir = new File(url.getPath());
                String[] fileNames = dir.list();
                for (String fileName : fileNames) {

                    try (InputStream is = classLoader.getResourceAsStream(MESSAGES_DIR + fileName);) {
                        read(is);
                    } catch (IOException ex) {
                        // 例外処理
                    }
                }
            }
        }
    }

    private static void read(InputStream is) {
        try (InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);) {
            // 読み込み処理はちゃんと書く。。。
            String line = null;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException ex) {
            // 例外処理
        }
    }

}

動作確認に使用したリソース

JARの構成
ReadMessagesInClasspath.jar
├META-INF/
| └MANIFEST.MF
├jp/
| └gr/
|  └java_conf/
|   └ksino/
|    └readmessages/
|     └Main.class
└messages/
 ├a.properties
 └b.properties
a.properties
aaa=bbb
ccc=ddd
b.properties
zzz=xxx
yyy=vvv
www=123

動作確認結果

$ java -cp ReadMessagesInClasspath.jar jp.gr.java_conf.ksino.readmessages.Main
aaa=bbb
ccc=ddd
zzz=xxx
yyy=vvv
www=123