Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the list of filenames from resource folder when running in jar

I have some Json files in the folder "resource/json/templates". I want to read these Json files. So far, the snippet of code below is allowing me to do so when running the program in IDE but it fails when I run it in the jar.

  JSONParser parser = new JSONParser();
  ClassLoader loader = getClass().getClassLoader();
  URL url = loader.getResource(templateDirectory);
  String path = url.getPath();
  File[] files = new File(path).listFiles();
  PipelineTemplateRepo pipelineTemplateRepo = new PipelineTemplateRepoImpl();
  File templateFile;
  JSONObject templateJson;
  PipelineTemplateVo templateFromFile;
  PipelineTemplateVo templateFromDB;
  String templateName;


  for (int i = 0; i < files.length; i++) {
    if (files[i].isFile()) {
      templateFile = files[i];
      templateJson = (JSONObject) parser.parse(new FileReader(templateFile));
      //Other logic
    }
  }
}
catch (Exception e) {
  e.printStackTrace();
}

Any help would be greatly appreciated.

Thanks a lot.

like image 526
Juvenik Avatar asked Dec 12 '17 14:12

Juvenik


2 Answers

Assuming that in the class path, in the jar the directory starts with /json (/resource is a root directory), it could be as such:

    URL url = getClass().getResource("/json");
    Path path = Paths.get(url.toURI());
    Files.walk(path, 5).forEach(p -> System.out.printf("- %s%n", p.toString()));

This uses a jar:file://... URL, and opens a virtual file system on it.

Inspect that the jar indeed uses that path.

Reading can be done as desired.

     BufferedReader in = Files.newBufferedReader(p, StandardCharsets.UTF_8);
like image 158
Joop Eggen Avatar answered Oct 17 '22 11:10

Joop Eggen


Firstly, remember that Jars are Zip files so you can't get an individual File out of it without unzipping it. Zip files don't exactly have directories, so it's not as simple as getting the children of a directory.

This was a bit of a difficult one but I too was curious, and after researching I have come up with the following.

Firstly, you could try putting the resources into a flat Zip file (resource/json/templates.zip) nested in the Jar, then loading all the resources from that zip file since you know all the zip entries will be the resources you want. This should work even in the IDE.

String path = "resource/json/templates.zip";
ZipInputStream zis = new ZipInputStream(getClass().getResourceAsStream(path));
for (ZipEntry ze = zis.getNextEntry(); ze != null; ze = zis.getNextEntry()) {
    // 'zis' is the input stream and will yield an 'EOF' before the next entry
    templateJson = (JSONObject) parser.parse(zis);
}

Alternatively, you could get the running Jar, iterate through its entries, and collect the ones that are children of resource/json/templates/ then get the streams from those entries. NOTE: This will only work when running the Jar, add a check to run something else while running in the IDE.

public void runOrSomething() throws IOException, URISyntaxException {
    // ... other logic ...
    final String path = "resource/json/templates/";
    Predicate<JarEntry> pred = (j) -> !j.isDirectory() && j.getName().startsWith(path);

    try (JarFile jar = new Test().getThisJar()) {
        List<JarEntry> resources = getEntriesUnderPath(jar, pred);
        for (JarEntry entry : resources) {
            System.out.println(entry.getName());
            try (InputStream is = jar.getInputStream(entry)) {
                // JarEntry streams are closed when their JarFile is closed,
                // so you must use them before closing 'jar'
                templateJson = (JSONObject) parser.parse(is);
                // ... other logic ...
            }
        }
    }
}


// gets ALL the children, not just direct
// path should usually end in backslash
public static List<JarEntry> getEntriesUnderPath(JarFile jar, Predicate<JarEntry> pred)
{
    List<JarEntry> list = new LinkedList<>();
    Enumeration<JarEntry> entries = jar.entries();

    // has to iterate through all the Jar entries
    while (entries.hasMoreElements()) {
        JarEntry entry = entries.nextElement();
        if (pred.test(entry))
            list.add(entry);
    }
    return list;
}


public JarFile getThisJar() throws IOException, URISyntaxException {
    URL url = getClass().getProtectionDomain().getCodeSource().getLocation();
    return new JarFile(new File(url.toURI()));
}

I hope this helps.

like image 36
xtratic - Reinstate Monica Avatar answered Oct 17 '22 13:10

xtratic - Reinstate Monica