1

I have migrated a Java project to use Gradle, and it works just fine in IntelliJ or using the command line.

However when I package it into a standalone JAR using FatJar, I have some issues accessing the ressources of my project.

Here is the current code. It recursively lists the .sql files from the queries folder, and for each file it puts it in a Map.

private static void loadQueriesFrom(final String baseDir) throws IOException {
    final InputStream is = Queries.class.getResourceAsStream(baseDir);

    if (is == null)
        return;
    
    List<String> queryFiles = IOUtils.readLines(is, Charsets.UTF_8);
    
    for (String queryFile: queryFiles) {
        final int index = queryFile.lastIndexOf(".sql");
        
        if (index == -1) {
            loadQueriesFrom(baseDir + queryFile + "/"); // directory
        }
        else {
            final String queryName = queryFile.substring(0, index).toLowerCase();

            queries.put(baseDir.substring(QUERY_DIRECTORY.length()) + queryName, IOUtils.toString(Queries.class
                    .getResourceAsStream(baseDir + queryFile)));

        }
    }
       
}

// First call is
loadQueriesFrom("/queries");

My directory structure is as follows:

- src
  - main
      - java
      - resources
          - queries
  - test

This piece of code works fine except when it is packaged in a JAR: in this case, is (line 1) is always null. Here's my fatjar configuration:

task fatjar(type: Jar) {
    baseName = project.name
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
    manifest {
        attributes 'Implementation-Title': 'My project name', 'Implementation-Version': version
        attributes 'Main-Class': 'org.mypackage.MyMainClass'
    }
}

If I open the .jar generated file, the structure looks like

- org
    - mypackage
        ...
- queries

So the queries folder is included in the JAR but for some reason getResourceAsStream can't list it. However, it can access individual files. If I insert the following line, I get the intended result.

Queries.class.getResourceAsStream("/queries/collaborators/get_collab_list.sql")

What I've tried:

  • changing /queries to queries

  • changing getResourceAsStream(...) by getClassLoader().getResourceAsStream(...)

  • both

    I've seen this answer, but it looks a bit overkill.

Help appreciated. Thanks!

Community
  • 1
  • 1
christophetd
  • 3,834
  • 20
  • 33
  • take a look at this SO answer: [http://stackoverflow.com/questions/5193786/how-to-use-classloader-getresources-correctly/5194049#5194049](http://stackoverflow.com/questions/5193786/how-to-use-classloader-getresources-correctly/5194049#5194049) but instead of converting it to a file just use the stream of URL.openStream to read the content. – daotan Aug 25 '16 at 13:00

1 Answers1

2

Turn the directory resource into a File. Ignoring exceptions, in its most basic form could look something like this:

URL url = Queries.class.getResource("/queries");
File dir = new File(url.toURI());
for (File file : dir.listFiles()) {
    // do something with file
}

If you want to recurse the file tree:

void doSomething(File file) {
    // do something with file
}

void processFile(File file) {
    if (file.isDirectory()) {
        for (File sub : file.listFiles()) {
            processFile(sub);
        }
    } else {
        doSomething(file);
    }
}

then

URL url = Queries.class.getResource("/queries");
File dir = new File(url.toURI());
processFile(dir);
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • Hey, your solution didn't work, the File constructor threw an exception saying the URI is invalid (_not hierarchical_). I have changed the way the queries are loaded (lazy loading + caching instead of pre-loading everything). I'll give you the bounty, though, to thank you for your answer and since I can't take it back anyway. :-) – christophetd Aug 24 '16 at 15:43
  • (I can't give the bounty yet, though) – christophetd Aug 24 '16 at 15:44